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. Я ожидаю, что проблема будет в одной из этих служб. Но я понятия не имею, как это обойти.
Проведенные исследования:
- Учебник Microsoft по Python в SQL Server: docs.microsoft.com
- Архитектура SQL Server 2017 для запуска Python: https://docs.microsoft.com/en-us/sql/advanced-analytics/python/new-components-in-sql-server-to-support-python.-integration?view=sql-server-2017
- Поддержка Pandas целого числа
NA
: http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na
Редактировать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.