Разница между SQLite и PROC SQL при оценке пустого пространства



Я сравниваю два набора данных, чтобы найти повторяющиеся записи в определенных столбцах.



Я сделал это сначала в SAS, используя команду PROC SQL, как показано ниже (что я считаю истинным результатом), используя следующий запрос:



proc sql;
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2;
quit;


Я вывожу этот результат в csv, давая output_sas.csv



Я также сделал это в Python, используя SQLite3, используя тот же запрос:



conn = sqlite3.connect(file_path + db_name)
cur = conn.cursor()
cur.execute("""
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2
""")


Я вывожу это в csv, давая output_python.csv.



Проблема:



Выходы должны быть следующие: то же самое, но они не :



output_sas.csv содержит 123 больше записей , чем output_python.csv.



В выходном файле SAS есть 123 записи, которые содержат пустое пространство "" в Столбцах yob1 и yob2, т. е. в качестве примера 123 записи в sas_data.csv выглядят следующим образом:



yob1 yob2 cob1 cob2 surname1 surname2
"" "" 1 1 xx xx
"" "" 2 2 yy yy
.
.
.
# Continues for 123 records


Я нахожу, что эта разница обусловлена столбцами yob1 и yob2, которые в приведенных выше 123 записях содержат пустое пространство. эти 123 пары записей отсутствуют из файла output_python.csv.

[Примечание: В этой работе строка нулевой длины соответствует отсутствующему значению]



Короче говоря:



Процедура PROC SQL в SAS оценивает пустое пространство как равное, т. е. "" == "" -> TRUE.



Код Python SQLite, по-видимому, делает противоположное, т. е. "" == "" ->
FALSE



Это происходит даже несмотря на то, что "" == "" -> True в Python.



Вопрос:



Почему это так и что мне нужно изменить, чтобы соответствовать вверх по выходу SQLite к выходу PROC SQL?



Примечание: обе подпрограммы используют одни и те же входные наборы данных. Они полностью равны, и я даже вручную изменяю код Python, чтобы гарантировать, что столбцы yob1 и yob2 содержат "" для пропущенных значений.

Обновление 1:



На данный момент мой SAS PROC SQL код работает на uses data1.sas7bdat, named local и data2.sas7bdat, named neighbor.



Чтобы использовать один и тот же набор данных в Python, в SAS, я экспортирую их в csv и читаю в Питон.



Если я это сделаю:



import pandas as pd
# read in
dflocal = pd.read_csv(csv_path_local, index_col=False)
dfneighbor = pd.read_csv(csv_path_neighbor, index_col=False)


Панды преобразуют пропущенные значения в nan. Мы можем использовать isnull(), чтобы найти количество значений nan в каждом из столбцов:



# find null / nan values in yob1 and yob2 in each dataset
len(dflocal.loc[dflocal.yob1.isnull()])
78
len(dfneighbor.loc[dfneighbor.yob2.isnull()])
184


Чтобы решить проблему нулевого значения, я затем явно преобразую nan в строку нулевой длины "", выполнив:



dflocal['yob1'].fillna(value="", axis=0, inplace=True)
dfneighbor['yob2'].fillna(value="", axis=0, inplace=True)


Мы можем проверить, обновились ли значения, проверив известный nan:



dflocal.iloc[393].yob1
`""`
type(dflocal.iloc[393].yob1)
str


Таким образом, они представляют собой строку длины 0.



Затем считайте их в SQL via:



dflocal.to_sql('local', con=conn, flavor='sqlite', if_exists='replace', index=False)
dfneighbor.to_sql('neighbor', con=conn, flavor='sqlite', if_exists='replace', index=False)


Затем выполните тот же код SQLite3:



conn = sqlite3.connect(file_path + db_name)
cur = conn.cursor()
cur.execute("""
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2
""")


Несмотря на то, что я сделал это явное изменение я все еще получите те же самые пропущенные 123 значения, хотя нулевые значения были изменены на строку нулевой длины "".



Потенциальное Решение :



Однако, если я вместо этого импортирую набор данных с аргументом na_filter=False, это делает преобразование из null в "" для меня.



dflocal     = pd.read_csv(csv_path_local, index_col=False, na_filter=False)
dfneighbor = pd.read_csv(csv_path_neighbor, index_col=False, na_filter=False")

# find null / nan values in yob1 and yob2 in each dataset

len(dflocal.loc[dflocal.yob1.isnull()])
0
len(dfneighbor.loc[dfneighbor.yob2.isnull()])
0


Когда я импортирую их наборы данных в мою базу данных и выполнить это через тот же SQL-код:



conn = sqlite3.connect(file_path + db_name)
cur = conn.cursor()
cur.execute("""
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2
""")


УРА, Я ПОЛУЧАЮ ТОТ ЖЕ РЕЗУЛЬТАТ, ЧТО И КОД SAS!



Но почему первое решение не работает? Я делаю то же самое в обоих случаях (первый делает это вручную с помощью fill_na, а второй-с помощью na_filter=False).
625   2  

2 ответов:

Похоже, что вас поразило то, как SQLite3 (и большинство СУБД) обрабатывают нулевые значения. В SAS можно сравнить нулевые значения с фактическими, но в большинстве систем СУБД это невозможно. Таким образом, в SAS комплементарные логические сравнения типа (A=B) и (a ne B) всегда будут давать одно как истинное, а другое как ложное. Но в СУБД, когда либо A, либо B, либо оба равны нулю, то оба (A=B) и (a ne B) будут ложными. Нулевое значение не меньше и не больше другого значения.

В SAS, если оба значения равны нулю, тогда они равны, и это одно равно нулю, а другое нет, тогда они не равны. Нулевые числовые значения меньше любого фактического числа. Нулевые символьные переменные не существуют и поэтому просто обрабатываются как пустое заполненное значение. Обратите внимание, что SAS также игнорирует конечные пробелы при сравнении символьных переменных.

На практике это означает, что вам нужно добавить дополнительный код для обработки нулевых значений при запросе СУБД.
ON (a.surname1 = b.surname2 or (a.surname1 is null and b.surname1 is null))
AND (a.yob1 = b.yob2 or (a.yob1 is null and b.yob2 is null))
AND (a.cob1 = b.cob2 or (a.cob1 is null and b.cob2 is null))

В SAS на самом деле нет понятия нулевых значений для символов. Это скорее пустая строка. Однако в большинстве реализаций SQL (включая SQlite, я полагаю) значение null и пустая строка будут отличаться.

Пустое значение в SAS действительно оценивается как "" = "" , которое является true

Однако в вашей средней СУБД то, что вы назвали бы "пустыми значениями", часто является значениями null, а не пустыми строками (""). и null=null не является истинным. Вы не можете сравнивать нулевые значения с чем угодно, включая нулевые значения.

Что вы могли бы сделать, так это изменить свой SQlite на

CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
    local AS a INNER JOIN neighbor AS b
    ON a.surname1 = b.surname2
    AND coalesce(a.yob1,'') = coalesce(b.yob2,'')
    AND a.cob1 = b.cob2

Функция coalesce заменит yob пустой строкой, если yob равно null.

Однако имейте в виду, что, если yob1 равно null и yob2 на самом деле является пустой строкой, добавление этих коалесцирующих функций изменит то, что было бы условием null='', которое не является истинным, на ''='', которое является истинным. Если это не то, что вы хотели бы, вы также можете просто написать это как это:
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
    local AS a INNER JOIN neighbor AS b
    ON a.surname1 = b.surname2
    AND (a.yob1 = b.yob2
        OR (a.yob1 is null AND a.yob2 is null)
        )
    AND a.cob1 = b.cob2

Comments

    Ничего не найдено.