Python в SQL Server: значения NULL в столбцах INT сопоставляются с -2147483648, а не с None

tl;dr

Я работаю с Python в SQL Server 2017. Код Python заключен в хранимую процедуру, которой я передаю запрос. Запрос оценивается, и данные передаются в Python. Если строковый столбец (char, nchar, varchar, nvarchar) в запросе содержит NULL, в Python он сопоставляется с None. Но если столбец int содержит NULL, он сопоставляется с -2147483648 (я думаю, минимальное целочисленное значение).

Мой вопрос заключается в том, как получить значение NULL из столбца int, чтобы оно было None в Python, а не -2147483648? Столбец должен оставаться int.

Воспроизводимый пример

Тестовые данные, с которыми я работаю:

CREATE TABLE [dbo].[test_table](
    [a-string] [nvarchar](50) NULL,
    [a-date] [date] NULL,
    [a-int] [int] NULL,
    [a-null-int] [int] NULL,
    [a-null-str] [nvarchar](50) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[test_table] ([a-string], [a-date], [a-int], [a-null-int], [a-null-str]) VALUES (N'asdf', CAST(N'2018-04-11' AS Date), 1, NULL, NULL)
INSERT [dbo].[test_table] ([a-string], [a-date], [a-int], [a-null-int], [a-null-str]) VALUES (N'fdsa', CAST(N'2008-04-11' AS Date), 2, NULL, NULL)
INSERT [dbo].[test_table] ([a-string], [a-date], [a-int], [a-null-int], [a-null-str]) VALUES (N'Bob "Bla" Bob', CAST(N'2028-04-11' AS Date), 3, NULL, NULL)
INSERT [dbo].[test_table] ([a-string], [a-date], [a-int], [a-null-int], [a-null-str]) VALUES (N'Bob, Bob', CAST(N'2038-04-11' AS Date), 4, NULL, NULL)
INSERT [dbo].[test_table] ([a-string], [a-date], [a-int], [a-null-int], [a-null-str]) VALUES (N'Bob bob', CAST(N'1998-04-11' AS Date), 5, 1, NULL)

Два последних столбца содержат несколько значений NULL. Первый типа int, второй типа nvarchar.

Код хранимой процедуры:

CREATE PROCEDURE [dbo].[usp_test]
    @query NVARCHAR(max)
AS
BEGIN
EXEC sp_execute_external_script 
@language = N'Python', 
@script = N'
print(InputDataSet)
',
@input_data_1 = @query
END;

Хранимая процедура имеет один параметр с запросом, который передает результат запроса в код Python. В коде Python я печатаю данные.

Как я выполняю хранимую процедуру:

EXEC [dbo].[usp_test] N'SELECT [a-string],CAST([a-date] as nvarchar(20)) as [a-date],[a-int],[a-null-int],[a-null-str] FROM [dbo].[test_table]'

Результат:

        a-string      a-date  a-int  a-null-int a-null-str
0           asdf  2018-04-11      1 -2147483648       None
1           fdsa  2008-04-11      2 -2147483648       None
2  Bob "Bla" Bob  2028-04-11      3 -2147483648       None
3       Bob, Bob  2038-04-11      4 -2147483648       None
4        Bob bob  1998-04-11      5           1       None

Непредвиденное поведение находится в столбце a-null-int. Как мне сделать его None, а не -2147483648, оставаясь при этом int?

Этот вопрос тесно связан с SQL Server. Согласно эта документация от Microsoft, BxlServer или SQL Satellite (не уверен) обрабатывает передачу данных между SQL Server и Python. Я ожидаю, что проблема будет в одной из этих служб. Но я понятия не имею, как это обойти.

Проведенные исследования:

Редактировать1; Является ли этот вопрос дубликатом вопроса Как сохранить пустое значение в виде целочисленного поля?

ИМО нет. Кажется, проблема заключалась в несоответствии типов данных (str vs int). Это не тот случай здесь. Если я проверю тип данных, я получаю:

print(type(InputDataSet.ix[0,"a-null-int"]))
>>> <class 'numpy.int32'>

Это правильно. Я передаю столбец int, и он сопоставляется с питоном int. Но мне нужно, чтобы это было None.

Редактировать2; Ответ на ответ @arun-gurunathan:

Прежде чем я начну, я должен заявить, что столбец [a-null-int] должен оставаться целочисленным. Для контекста мне нужно экспортировать данные в CSV. Чтобы проиллюстрировать мою проблему, я изменил значение последней строки в столбце [a-null-int] с NULL на 1. Соответственно изменено начало вопроса.

С помощью RxMissingValues.int32() я получаю значение, используемое для замены значений NULL, то есть -2147483648. Я могу заменить эти значения на numpy.NaN. Это не пуленепробиваемое исправление, потому что что произойдет, если столбец в SQL Server будет содержать именно это значение? Тем не менее, я продолжал идти по этому пути...

Следующий код я поместил в хранимую процедуру выше:

import numpy
from revoscalepy import RxMissingValues
InputDataSet.loc[InputDataSet["a-null-int"] == RxMissingValues.int32(), ("a-null-int")] = numpy.NaN
print(InputDataSet)

Вот что у меня получилось (сокращенно):

   a-null-int
0         NaN
1         NaN
2         NaN
3         NaN
4         1.0

Столбец [a-null-int] преобразуется в float. Это поведение задокументировано в документе pandas и обсуждалось на переполнение стека.

Я ожидаю, что моя проблема не разрешима из-за ограничений NumPy в обработке значений NA. Я подожду еще немного, чтобы увидеть, появятся ли еще ответы о том, как я могу сохранить тип столбца a-null-int как int, или какое-то обходное решение. В противном случае я приму ответ @arun-gurunathan.


person Simon Lang    schedule 19.04.2018    source источник
comment
Возможный дубликат Как сохранить пустое значение в виде целочисленного поля   -  person Jacob H    schedule 19.04.2018
comment
@JacobH Я так не думаю. Добавил объяснение в конец моего вопроса.   -  person Simon Lang    schedule 19.04.2018


Ответы (1)


документ rxMissingValues описывает панды /numpy ограничение хранения значений None в целочисленных столбцах. Вы можете справиться с этим, проверив отсутствующее значение (rxMissingValues.int32()), как описано в документе.

person Arun Gurunathan    schedule 19.04.2018
comment
Спасибо за ответ, он помог мне в дальнейшем расследовании. Добавил Edit2 в вопрос. - person Simon Lang; 20.04.2018