فهرست مقاله
دو دستهٔ داده از یک آزمایشگاهِ ساختگی رسیده است. در هر دسته، شناسهٔ حسگر و ولتاژِ گزارششده را داریم؛ اما شناسهٔ مستقلِ مشاهده و زمانِ ثبت همراهشان نیست. هنگامِ کنار هم گذاشتنِ دستهها، ممکن است بخواهیم ردیفهای تکراری را حذف کنیم. مشکل این است که «ردیفِ برابر» و «همان رویداد» دو تعریفِ متفاوتاند. این درس با یک آزمایشِ حافظهایِ SQLite نشان میدهد انتخابِ UNION یا UNION ALL دقیقاً چه چیزی را عوض میکند؛ هیچ داده یا ابزارِ واقعی در کار نیست.
۱. ردیفِ برابر الزاماً رویدادِ تکراری نیست

ستونِ اول، شناسهٔ قراردادیِ دستگاه است و ستونِ دوم، عددِ صحیحِ میلیولت یا مقدارِ نامعلوم. مثلاً ۱۲۰۰ میلیولت برابرِ ۱٫۲ ولت است؛ این انتخابِ واحد، ادعای دقتِ سختافزار نیست. ممکن است حسگر در دو لحظهٔ جدا همین مقدار را دیده باشد. اگر زمان یا شناسهٔ مشاهده را از داده کنار گذاشته باشیم، از دو ردیفِ برابر نمیتوانیم بفهمیم یک بسته دوباره فرستاده شده یا دو مشاهدهٔ واقعی داشتهایم.
در شاهدِ دستی، دستهٔ چپ شامل ردیفهای (۱،۱۲۰۰)، (۱،۱۲۰۰) و (۲،NULL) است. دستهٔ راست، (۱،۱۲۰۰)، (۲،NULL) و (۲،۰) را دارد. در مجموع شش رخدادِ ورودی وجود دارد. سه رخداد مقدارِ یکسانِ حسگرِ یک را حمل میکنند و دو رخداد، مقدارِ نامعلومِ حسگرِ دو را. NULL را ولتاژِ صفر یا نبودنِ خودِ ردیف معنی نمیکنیم؛ ردیفِ دارای صفر جداگانه حاضر است.
برای همین مرزِ هویت، آزمایشِ ثبتِ بستههای حسگر و تکرارِ قراردادی مکملِ این درس است. آن برنامه برای بسته یک کلیدِ دستگاه و شماره دارد و تعارضِ محتوای یک هویت را بررسی میکند. اینجا چنین کلیدی نداریم. انتخابِ عملگرِ ترکیب، جای طراحیِ هویتِ رخداد، سیاستِ اصلاحِ داده یا تشخیصِ تکرارِ شبکه را نمیگیرد.
۲. UNION ALL تعداد را نگه میدارد؛ UNION کلِ ردیف را مقایسه میکند

بخشِ پرسوجوهای مرکبِ مرجعِ SQLite میگوید UNION ALL همهٔ ردیفهای دو نتیجه را نگه میدارد، ولی UNION ردیفهای تکراریِ نتیجهٔ نهایی را حذف میکند. تکرار فقط میانِ دو دسته بررسی نمیشود؛ دو ردیفِ برابر در خودِ دستهٔ چپ نیز زیرِ UNION به یک نماینده میرسند. در شاهدِ ما، نتیجهٔ ALL شش ردیف و نتیجهٔ UNION سه ردیف است.
سه ردیفِ متمایز عبارتاند از (۱،۱۲۰۰)، (۲،NULL) و (۲،۰). برای حذفِ تکرار در همین پرسوجوی مرکب، دو NULL برابر در نظر گرفته میشوند و از مقدارِ غیرNULL جدا هستند. این قاعده را به مقایسهٔ معمولِ = یا پذیرشِ ردیف در WHERE تعمیم ندهید. درسِ سطحِ رکورد، NULL و شمارشِ JOIN آن مرزِ فیلتر و تفاوتِ مقدارِ نامعلوم با فرزندِ غایب را جدا باز میکند.
برابری به تمامِ ستونهای انتخابشده مربوط است. دو ردیفِ یک حسگر با ولتاژهای ۱۲۰۰ و ۱۲۰۱ زیرِ UNION باقی میمانند، چون ستونِ دوم فرق دارد. در مقابل، اگر فقط ستونِ شناسه را انتخاب کنیم، هر دو به شناسهٔ یک تبدیل میشوند. در شاهدِ ششردیفی، آن پرسوجوی سوم فقط دو شناسه میدهد. این خروجی تعدادِ دستگاههای حاضر در تصویرِ انتخابشده است، نه تعدادِ مشاهدهها یا تازهترین مقدارِ هر دستگاه.
۳. آزمایشِ محدود با SQLite و خروجیِ مرتبِ عمدی

تابع compare_reading_unions دو tuple دقیق، هر کدام با حداکثر شش ردیف میپذیرد. هر ردیف دقیقاً دو عضو دارد: شناسهٔ صحیح از یک تا چهار، و میلیولتِ صحیح از منفیِ دوهزار تا دوهزار یا None. bool، عددِ اعشاری، رشته و زیرکلاسها پذیرفته نیستند. این سقفها قراردادِ آموزشیِ خودماناند؛ محدودیتِ SQLite یا دامنهٔ یک حسگرِ واقعی نیستند. تمامِ ورودیها پیش از بازکردنِ اتصال بررسی میشوند، حتی آخرین ردیفِ دستهٔ دوم.
برنامه اتصالِ خصوصیِ :memory: میسازد و با سه دستورِ ثابت، نتیجهٔ ALL، نتیجهٔ ردیفِ کامل و نتیجهٔ فقط شناسه را میخواند. طبق مرجعِ sqlite3 پایتون، None و int به NULL و INTEGER میرسند؛ مقدارهای درج با جاینگهدار متصل میشوند. هیچ نامِ جدول، متنِ SQL یا اتصالِ دلخواهی از ورودی نمیگیریم. اتصال در finally بسته میشود و فایل یا شبکهای باز نمیشود.
خروجیِ SQL بدون ORDER BY ترتیبِ تضمینشده ندارد. کد، ردیفهای دریافتشده را عمداً برای مقایسه مرتب میکند: ابتدا شناسه، سپس مقدارِ نامعلوم و بعد مقدارِ صحیح. این ترتیب، قراردادِ گزارش است؛ ترتیبِ رسیدنِ بسته، ترتیبِ منبع یا زمانِ رویداد نیست. حتی UNION ALL را مجوزِ فرضِ «اول همهٔ چپ، بعد همهٔ راست» نمیگیریم. نسخهٔ Python و کتابخانهٔ SQLite متصل نیز از محیطِ همان اجرا در خروجی میآیند.
"""Whole projected-row UNION semantics for supplied synthetic integer readings."""
AUTHOR_MAIN_ENABLED = True
from dataclasses import FrozenInstanceError, asdict, dataclass
import itertools
import json
import platform
import sqlite3
import sys
MAX_ROWS = 6
MAX_SENSOR_ID = 4
MIN_MV = -2000
MAX_MV = 2000
ALL_SQL = ("SELECT sensor_id, voltage_mv FROM batch_left UNION ALL "
"SELECT sensor_id, voltage_mv FROM batch_right")
UNION_SQL = ("SELECT sensor_id, voltage_mv FROM batch_left UNION "
"SELECT sensor_id, voltage_mv FROM batch_right")
IDS_SQL = ("SELECT sensor_id FROM batch_left UNION "
"SELECT sensor_id FROM batch_right")
@dataclass(frozen=True)
class UnionReport:
union_all_rows: tuple
union_rows: tuple
sensor_ids: tuple
@property
def all_count(self):
return len(self.union_all_rows)
@property
def distinct_count(self):
return len(self.union_rows)
@property
def identity_count(self):
# Number of projected sensor IDs, not number of measurement events.
return len(self.sensor_ids)
def admit_batches(left_rows, right_rows):
"""Entire exact primitive envelope checked before sqlite3.connect."""
if type(left_rows) is not tuple or type(right_rows) is not tuple:
raise TypeError("each batch must be an exact materialized tuple")
if len(left_rows) > MAX_ROWS or len(right_rows) > MAX_ROWS:
raise ValueError("at most six supplied rows per batch")
for batch in (left_rows, right_rows):
for row in batch:
if type(row) is not tuple:
raise TypeError("each row must be an exact tuple")
if len(row) != 2:
raise ValueError("each row has sensor_id and voltage_mv")
identifier, value = row
if type(identifier) is not int:
raise TypeError("sensor ID must be an exact int, excluding bool")
if not 1 <= identifier <= MAX_SENSOR_ID:
raise ValueError("sensor ID must be in 1..4")
if value is not None and type(value) is not int:
raise TypeError("mV must be an exact int or None")
if value is not None and not MIN_MV <= value <= MAX_MV:
raise ValueError("mV must be in -2000..2000 or None")
def canonical_rows(rows):
"""An authored comparison convention; never an SQL visitation-order claim."""
return tuple(sorted(rows, key=lambda r: (r[0], r[1] is not None,
0 if r[1] is None else r[1])))
def compare_reading_unions(left_rows, right_rows):
"""Query a fresh private memory DB; no event identity or chronology inferred.
Repeated values can represent separate observations. UNION compares the
complete chosen projection; it neither discovers event IDs nor validates
physical calibration. Only bounded native INTEGER/NULL values are tested.
"""
admit_batches(left_rows, right_rows)
connection = sqlite3.connect(":memory:", autocommit=True)
try:
connection.execute("CREATE TABLE batch_left(sensor_id INTEGER NOT NULL,"
"voltage_mv INTEGER)")
connection.execute("CREATE TABLE batch_right(sensor_id INTEGER NOT NULL,"
"voltage_mv INTEGER)")
connection.executemany("INSERT INTO batch_left VALUES(?,?)", left_rows)
connection.executemany("INSERT INTO batch_right VALUES(?,?)", right_rows)
all_rows = canonical_rows(connection.execute(ALL_SQL).fetchall())
distinct_rows = canonical_rows(connection.execute(UNION_SQL).fetchall())
identifiers = tuple(sorted(row[0] for row in connection.execute(IDS_SQL)))
return UnionReport(all_rows, distinct_rows, identifiers)
finally:
connection.close()
def occurrence_relation_reference(left_rows, right_rows):
"""Valid-fixture oracle: tagged occurrences and pairwise equivalence classes.
No SQL, production admission, canonicalizer, set-union, or main API is used
here. A row's class is represented by its earliest equivalent occurrence.
Missing/missing is explicit; there is no general SQL-predicate evaluator.
"""
occurrences = [(side, position, identifier, value)
for side, batch in enumerate((left_rows, right_rows))
for position, (identifier, value) in enumerate(batch)]
representatives = []
ids = []
for position, event in enumerate(occurrences):
identifier, value = event[2:]
earlier_equivalent = any(
other[2] == identifier and
((other[3] is None and value is None) or
(other[3] is not None and value is not None and other[3] == value))
for other in occurrences[:position])
if not earlier_equivalent:
representatives.append((identifier, value))
if not any(other[2] == identifier for other in occurrences[:position]):
ids.append(identifier)
ordering = lambda row: (row[0], -1 if row[1] is None else 0,
0 if row[1] is None else row[1])
all_values = [(event[2], event[3]) for event in occurrences]
return UnionReport(tuple(sorted(all_values, key=ordering)),
tuple(sorted(representatives, key=ordering)),
tuple(sorted(ids)))
def rejects(expected, function, *args):
try:
function(*args)
except expected:
return True
return False
def admission_controls():
class IntegerSubclass(int):
pass
class TupleSubclass(tuple):
pass
consumed = []
def lazy():
consumed.append(True)
yield (1, 0)
bad_types = [(None, ()), ([], ()), ((), []), (lazy(), ()),
(TupleSubclass(), ()), ((), TupleSubclass()),
(([1, 0],), ()), ((TupleSubclass((1, 0)),), ()),
(((True, 0),), ()), (((1.0, 0),), ()),
(((IntegerSubclass(1), 0),), ()), ((("1", 0),), ()),
(((1, True),), ()), (((1, 1.0),), ()),
(((1, IntegerSubclass(0)),), ()), (((1, "0"),), ()),
(((1, float("nan")),), ()), (((1, b"0"),), ()),
(((1, 0),), ((2, None), (3, True)))]
bad_values = [(((),), ()), (((1,),), ()), (((1, 0, 0),), ()),
(((0, 0),), ()), (((5, 0),), ()),
(((1, -2001),), ()), (((1, 2001),), ()),
(((1, 0),) * 7, ()), ((), ((1, None),) * 7),
(((1, 0),), ((2, None), (3, 2001)))]
calls = []
actual_connect = sqlite3.connect
def forbidden_connect(*args, **kwargs):
calls.append((args, kwargs))
raise AssertionError("database opened before whole admission")
try:
sqlite3.connect = forbidden_connect
good_types = all(rejects(TypeError, compare_reading_unions, *pair)
for pair in bad_types)
good_values = all(rejects(ValueError, compare_reading_unions, *pair)
for pair in bad_values)
finally:
sqlite3.connect = actual_connect
return {"passed": good_types and good_values and not calls and not consumed,
"type_profiles": len(bad_types), "value_or_shape_profiles": len(bad_values),
"database_connection_attempts": len(calls),
"generator_consumed": bool(consumed)}
def connection_controls():
actual_connect = sqlite3.connect
closed = []
inject = [False]
sentinel = RuntimeError("expected injected SELECT failure")
class TrackedConnection:
def __init__(self, connection):
self.connection = connection
def execute(self, statement, *args):
if inject[0] and statement == UNION_SQL:
raise sentinel
return self.connection.execute(statement, *args)
def executemany(self, *args):
return self.connection.executemany(*args)
def close(self):
closed.append(True)
self.connection.close()
def tracked_connect(*args, **kwargs):
return TrackedConnection(actual_connect(*args, **kwargs))
caught = None
try:
sqlite3.connect = tracked_connect
success = compare_reading_unions(((1, None),), ())
inject[0] = True
try:
compare_reading_unions(((1, 0),), ((2, 1),))
except RuntimeError as error:
caught = error
finally:
sqlite3.connect = actual_connect
return {"passed": success == UnionReport(((1, None),), ((1, None),), (1,))
and closed == [True, True] and caught is sentinel,
"actual_connections_closed": len(closed),
"controlled_error_identity_preserved": caught is sentinel}
def run_example():
checks = {}
def check(name, value):
if type(value) is not bool or not value:
raise AssertionError(name)
checks[name] = value
left = ((1, 1200), (1, 1200), (2, None))
right = ((1, 1200), (2, None), (2, 0))
manual = compare_reading_unions(left, right)
check("manual_six_occurrences_three_whole_rows_two_sensor_ids",
manual == UnionReport(((1, 1200),) * 3 + ((2, None),) * 2 + ((2, 0),),
((1, 1200), (2, None), (2, 0)), (1, 2)))
repeated = compare_reading_unions(((1, 7), (1, 7)), ())
check("duplicate_rows_inside_one_side_are_distinct_occurrences",
repeated.all_count == 2 and repeated.distinct_count == 1)
across = compare_reading_unions(((1, 7),), ((1, 7),))
check("cross_side_equal_rows_add_occurrences_but_not_classes",
across == repeated)
nulls = compare_reading_unions(((2, None), (2, 0)), ((2, None),))
check("missing_values_collapse_as_rows_and_stay_distinct_from_zero",
nulls.union_all_rows == ((2, None), (2, None), (2, 0))
and nulls.union_rows == ((2, None), (2, 0)))
sensors = compare_reading_unions(((1, 10),), ((2, 10),))
check("same_value_different_sensors_remains_two_whole_rows",
sensors.distinct_count == 2 and sensors.sensor_ids == (1, 2))
readings = compare_reading_unions(((1, 1200),), ((1, 1201),))
check("same_sensor_different_mV_remains_two_whole_rows",
readings.union_rows == ((1, 1200), (1, 1201)))
check("projecting_only_ID_discards_reading_distinctions",
readings.sensor_ids == (1,) and readings.distinct_count == 2)
check("both_empty_and_empty_side_do_not_invent_records",
compare_reading_unions((), ()) == UnionReport((), (), ())
and compare_reading_unions((), left) == compare_reading_unions(left, ()))
unordered = compare_reading_unions(((4, 2), (1, 0), (1, None)), ((1, -2),))
check("canonical_presentation_is_explicit_missing_first_then_integer",
unordered.union_all_rows == ((1, None), (1, -2), (1, 0), (4, 2)))
check("input_permutations_and_side_swap_preserve_canonical_multiplicity",
all(compare_reading_unions(a, b) == manual
for a, b in ((right, left), (tuple(reversed(left)), right),
(left, tuple(reversed(right))))))
alphabet = ((1, None), (1, 0), (2, 0))
forms = [p for n in range(4) for p in itertools.product(alphabet, repeat=n)]
profiles = rows = 0
for a in forms:
for b in forms:
actual = compare_reading_unions(a, b)
expected = occurrence_relation_reference(a, b)
if actual != expected:
raise AssertionError(("occurrence_equivalence", a, b, actual, expected))
profiles += 1
rows += len(actual.union_all_rows)
check("finite_tagged_occurrence_equivalence_family_ONE_group",
len(forms) == 40 and profiles == 1600 and rows == 8160)
invalid = admission_controls()
check("strict_whole_admission_before_database_and_no_coercion", invalid["passed"])
cleanup = connection_controls()
check("finally_closes_actual_connections_and_keeps_controlled_error",
cleanup["passed"])
maximum = ((1, -2000), (4, 2000), (2, None)) * 2
maximum_before = tuple(maximum)
bounded = compare_reading_unions(maximum, maximum)
fresh = compare_reading_unions(left, right)
check("caps_inputs_immutable_and_frozen_results_are_fresh",
maximum == maximum_before and bounded.all_count == 12
and bounded.distinct_count == 3 and bounded.sensor_ids == (1, 2, 4)
and fresh == manual and fresh is not manual
and rejects(FrozenInstanceError, setattr, fresh, "union_rows", ()))
assert len(checks) == 14 and all(type(x) is bool and x for x in checks.values())
return {"runtime": {"python": platform.python_version(),
"implementation": platform.python_implementation(),
"sqlite_library": sqlite3.sqlite_version},
"scope": "fixed SQL on fresh :memory: only; no real sensor, file, network or public benchmark",
"input_contract": {"batch_length": "exact tuple0..6 per side",
"sensor_id": "exact int1..4, bool excluded",
"voltage_mv": "None or exact int-2000..2000; synthetic millivolts",
"ordering": "authored canonical sort only, not event chronology or SQL arrival order"},
"manual_inputs": {"left": left, "right": right},
"manual_report": asdict(manual),
"manual_counts": {"all_occurrences": manual.all_count,
"distinct_complete_rows": manual.distinct_count,
"distinct_projected_sensor_ids": manual.identity_count},
"same_sensor_two_readings_report": asdict(readings),
"independent_family": {"profiles": profiles, "occurrences_compared": rows,
"forms_per_side": len(forms), "counted_as_check_groups": 1},
"invalid_inputs": invalid, "resource_controls": cleanup,
"meaningful_named_groups": len(checks), "checks": checks,
"limits": ["Equal projected values do not establish duplicate physical events or authenticated identity.",
"NULL equality is scoped to compound duplicate elimination, not ordinary SQL predicates.",
"No general collation/affinity/float equality, query-plan, performance or concurrency claim.",
"The finite oracle family is evidence for these supplied fixtures, not a universal SQLite proof."]}
if __name__ == "__main__":
assert AUTHOR_MAIN_ENABLED is True, "Await ROOT registered canonical sole-author authorization"
sys.stdout.reconfigure(encoding="utf-8", newline="\n")
result = run_example()
sys.stdout.buffer.write((json.dumps(result, ensure_ascii=True, indent=2) + "\n").encode("utf-8"))
۴. پاسخِ مستقل را از همان SQL نگیریم

اگر انتظارِ UNION را با اجرای دوبارهٔ همان پرسوجو بسازیم، یک خطای مشترک میتواند هر دو مسیر را تأیید کند. مرجعِ این برنامه به هر ورودی، نشانیِ دسته و جایگاه میدهد؛ بنابراین رخدادهای هممقدار را از ابتدا جدا دارد. سپس با رابطهٔ برابریِ دو ستونی، برای هر ردیف میپرسد آیا پیشتر عضوِ همارزی داشته است. اولین عضو نماینده میشود و برای ALL تمامِ رخدادها باقی میمانند؛ این مرجع SQL اجرا نمیکند.
خانوادهٔ متناهی از سه نوعِ ردیفِ قراردادی، دنبالههای طولِ صفر تا سه را برای هر سمت میسازد. این خانواده یک گروه از چهارده گروهِ معنایی است، نه هزاران گروهِ جدا. شاهدِ ششردیفی، تکرارِ درون و میانِ دستهها، NULL و صفر، تغییرِ تصویرِ ستونها، ورودیِ خالی، ترتیبِ گزارش، مرزِ پذیرش و بستنِ اتصال نیز جدا بررسی میشوند. تعدادِ واقعیِ حالتهای اجراشده و خروجیِ کامل در گزارش ثبت میشود؛ خانواده، برهانِ همهٔ رفتارهای SQL نیست.
{
"runtime": {
"python": "3.14.5",
"implementation": "CPython",
"sqlite_library": "3.50.4"
},
"scope": "fixed SQL on fresh :memory: only; no real sensor, file, network or public benchmark",
"input_contract": {
"batch_length": "exact tuple0..6 per side",
"sensor_id": "exact int1..4, bool excluded",
"voltage_mv": "None or exact int-2000..2000; synthetic millivolts",
"ordering": "authored canonical sort only, not event chronology or SQL arrival order"
},
"manual_inputs": {
"left": [
[
1,
1200
],
[
1,
1200
],
[
2,
null
]
],
"right": [
[
1,
1200
],
[
2,
null
],
[
2,
0
]
]
},
"manual_report": {
"union_all_rows": [
[
1,
1200
],
[
1,
1200
],
[
1,
1200
],
[
2,
null
],
[
2,
null
],
[
2,
0
]
],
"union_rows": [
[
1,
1200
],
[
2,
null
],
[
2,
0
]
],
"sensor_ids": [
1,
2
]
},
"manual_counts": {
"all_occurrences": 6,
"distinct_complete_rows": 3,
"distinct_projected_sensor_ids": 2
},
"same_sensor_two_readings_report": {
"union_all_rows": [
[
1,
1200
],
[
1,
1201
]
],
"union_rows": [
[
1,
1200
],
[
1,
1201
]
],
"sensor_ids": [
1
]
},
"independent_family": {
"profiles": 1600,
"occurrences_compared": 8160,
"forms_per_side": 40,
"counted_as_check_groups": 1
},
"invalid_inputs": {
"passed": true,
"type_profiles": 19,
"value_or_shape_profiles": 10,
"database_connection_attempts": 0,
"generator_consumed": false
},
"resource_controls": {
"passed": true,
"actual_connections_closed": 2,
"controlled_error_identity_preserved": true
},
"meaningful_named_groups": 14,
"checks": {
"manual_six_occurrences_three_whole_rows_two_sensor_ids": true,
"duplicate_rows_inside_one_side_are_distinct_occurrences": true,
"cross_side_equal_rows_add_occurrences_but_not_classes": true,
"missing_values_collapse_as_rows_and_stay_distinct_from_zero": true,
"same_value_different_sensors_remains_two_whole_rows": true,
"same_sensor_different_mV_remains_two_whole_rows": true,
"projecting_only_ID_discards_reading_distinctions": true,
"both_empty_and_empty_side_do_not_invent_records": true,
"canonical_presentation_is_explicit_missing_first_then_integer": true,
"input_permutations_and_side_swap_preserve_canonical_multiplicity": true,
"finite_tagged_occurrence_equivalence_family_ONE_group": true,
"strict_whole_admission_before_database_and_no_coercion": true,
"finally_closes_actual_connections_and_keeps_controlled_error": true,
"caps_inputs_immutable_and_frozen_results_are_fresh": true
},
"limits": [
"Equal projected values do not establish duplicate physical events or authenticated identity.",
"NULL equality is scoped to compound duplicate elimination, not ordinary SQL predicates.",
"No general collation/affinity/float equality, query-plan, performance or concurrency claim.",
"The finite oracle family is evidence for these supplied fixtures, not a universal SQLite proof."
]
}
خطای خواندنِ کنترلشده در یک آزمون، فقط برای مشاهدهٔ finally و حفظِ همان استثنا تزریق میشود؛ آن را شکستِ واقعیِ داده یا سرویس معرفی نمیکنیم. گزارشِ خروجی نیز منجمد و ورودیها دستنخوردهاند. این مثال با عددِ صحیح و NULL کار میکند؛ برابریِ ممیزشناور، قواعدِ همسنجیِ متن، تبدیلِ نوعِ ضمنی، چند اتصالِ همزمان و سرعتِ پرسوجوها نتیجهٔ این آزمایش نیستند.
۵. انتخابِ معنا و چهار تمرینِ پاسخدار

پرسشِ اول: اگر چپ دو ردیفِ برابرِ (۱،۱۲۰۰) و راست خالی باشد، دو عملگر چه تعدادی میدهند؟ پاسخ: ALL دو رخداد و UNION یک ردیف میدهد. خالیبودنِ سمتِ راست، حذفِ تکرارِ داخلِ چپ را لغو نمیکند. این پاسخ هنوز نمیگوید در جهانِ واقعی یک مشاهده داشتهایم یا دو مشاهده؛ برای آن پرسش دادهٔ هویتیِ بیشتری لازم است.
پرسشِ دوم: چرا دو ردیفِ (۲,NULL) یکی میشوند، ولی (۲,NULL) و (۲,۰) جدا میمانند؟ پاسخ: در حذفِ تکرارِ پرسوجوی مرکب، دو مقدارِ تهی همردهاند، اما تهی با صفر فرق دارد. این تصمیم برای یکسانبودنِ ردیفِ انتخابشده است؛ نباید آن را پاسخِ درستِ هر عبارتِ شرطیِ دارای NULL فرض کنیم.
پرسشِ سوم: اگر یک حسگر ۱۲۰۰ و سپس ۱۲۰۱ میلیولت داشته باشد، UNION تازهترین مقدار را پیدا میکند؟ پاسخ: خیر. در تصویرِ دو ستونی هر دو ردیف میمانند و در تصویرِ فقط شناسه، اطلاعاتِ ولتاژ کنار میرود. هیچیک قانونِ تازهترین مشاهده ندارد؛ زمانِ معتبر، رفعِ تساوی و هویتِ رویداد باید در قراردادِ مستقلی بیان شوند.
پرسشِ چهارم: از ردیفهای مرتبشدهٔ گزارش میتوان منبع یا زمانِ ورود را بازسازی کرد؟ پاسخ: خیر. مرتبسازی فقط نمایشِ قابلمقایسه میسازد. اگر هدف، حفظِ همهٔ مشاهدههاست، ALL همراهِ ستونهای لازم برای هویت و منشأ انتخابِ معنادارتری است؛ اگر هدف مجموعهٔ مقادیرِ متمایزِ همین تصویر است، UNION همان پرسش را پاسخ میدهد. هیچکدام بهتنهایی اصالت، کالیبراسیون یا کفایتِ داده را تضمین نمیکنند.
منابع و مطالعهٔ بیشتر
بررسی منابع: ۳۰ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

