SQL Server, где сравнения предложений с различными типами и поведение приведения по умолчанию - PullRequest
5 голосов
/ 06 мая 2009

Правильно ... сегодня меня это озадачило, так что, может быть, один из ярких искр SQL Server может пролить свет на это поведение.

У нас есть стол Phones. В нем телефонные номера хранятся как nvarchars, и он содержит номера в международном формате, только в числовом формате ... поэтому номер США +1-(212)-999-9999 хранится как 12129999999

По не зависящим от меня причинам, кто-то написал SPROC, который взял номер телефона как bigint, не делал кастинг, делал простое выражение where = сравнение, и это работало абсолютно нормально, пока некоторые ненужные данные не попали в Столбец nvarchar на столе, который заставил его сломаться. Рассмотрим следующий тестовый скрипт.

IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Phones')
BEGIN
    DROP TABLE Phones
END
GO

CREATE TABLE [dbo].[Phones]
(
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Mobile] [nvarchar](50) NOT NULL,
    CONSTRAINT [PK_Phones] PRIMARY KEY CLUSTERED 
    ( [ID] ASC )
    WITH (
      PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
      IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
      ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
) ON [PRIMARY]
GO

DECLARE @biMobile_1 bigint
DECLARE @biMobile_2 bigint
SET @biMobile_1 = 12121111111
SET @biMobile_2 = 12129999999

Print 'Inserting Phone Number'
INSERT INTO Phones (Mobile) VALUES ('12121111111')

Print 'Selecting Phone Number'
SELECT * FROM Phones WHERE Mobile = @biMobile_1 --Select #1

Print 'Inserting Junk Data'
INSERT INTO Phones (Mobile) VALUES ('JUNK DATA')
INSERT INTO Phones (Mobile) VALUES ('12129999999')

Print 'Selecting From Table Containing Junk'
SELECT * FROM Phones WHERE Mobile = @biMobile_1 -- Select #2
SELECT * FROM Phones WHERE Mobile = @biMobile_2 -- Select #3

Первый выбор (помеченный # 1) будет работать Второй выбор (помеченный # 2) будет работать, но сразу же выдаст ошибку Третий выбор (помеченный # 3) ничего не возвращает.

Ошибка:

Error converting data type nvarchar to bigint.

Теперь это выглядит совершенно помешанным. Я думал, что это произойдет, это

  1. SQL реализует сравнение двух разных типов данных в предложении WHERE
  2. Будет предпринята попытка преобразовать @variable в тип данных столбца
  3. Если не получится, выведите ошибку, это работает, ОТЛИЧНО !!!

То, что на самом деле происходит, это

  1. SQL реализует сравнение двух разных типов данных в предложении WHERE
  2. Построчно он преобразует значение в столбце в тип данных переменной @ 1033 *
  3. Для каждого успешного преобразования он выполняет сравнение и возвращает эту строку.
  4. Если он попадает в значение в столбце, которое он не может преобразовать, он бомбардирует, возвращает все найденные данные и не перебирает таблицу.

Может ли кто-нибудь уточнить, что за логика стоит за этой логикой, и если есть какой-либо конкретный порядок приоритета, который SQL Server дает типам данных, когда он решает, что сравнивать / приводить

Примечание. Я провел этот тест в SQL 2005, но в SQL2K он также воспроизводим.

Ответы [ 6 ]

4 голосов
/ 06 мая 2009

Приоритет типа данных четко определен - http://msdn.microsoft.com/en-us/library/ms190309.aspx

Редактировать - чтобы уточнить, дело не в том, что sql всегда преобразует тип столбца в тип параметра. Это просто следует за приоритетом типа в ссылке, которую я дал. Это может означать, что параметр преобразуется в тип столбца, если это определяется приоритетом типа.

1 голос
/ 06 мая 2009

Лично вместо того, чтобы преобразовывать параметр в правильный тип, я бы объявил его как этот тип для начала.

1 голос
/ 06 мая 2009

Я не вижу, в чем проблема. Почему SQL-сервер знает, что запись # 232 из 1000 будет бомбить? Нет, пока не доберется до этой записи.

Тем временем, результаты передаются обратно клиенту по мере их генерирования. Это поможет с производительностью.

Что еще вы ожидаете?

0 голосов
/ 06 мая 2009

Проблема в том, что неявное преобразование из nvarchar в bigint недопустимо, только если содержимое nvarchar содержит нечисловые символы. Ядро базы данных не собирается выполнять запрос один раз, чтобы проверить каждое значение, которое будет возвращено , чтобы увидеть, является ли введенный вами запрос действительным для каждой строки, только чтобы затем развернуться и выполнить запрос снова вернуть результаты.

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

0 голосов
/ 06 мая 2009

Ваше логическое описание процесса действительно верно.

SQL Server - это простота, применяющая функцию Cast / Convert к мобильному столбцу (в данном случае неявное преобразование из nvarchar в bigint). Функции, конечно, применяются на основе строка за строкой , и, следовательно, поведение, которое вы наблюдаете, в результате чего оператор select не выполняет бомбардировку до тех пор, пока не прекратится функция преобразования.

Эту проблему можно избежать, преобразовав единственную числовую переменную, которая ищется (@ biMobile_2), в nvarchar, вместо того, чтобы неявно преобразовать столбец Mobile для всех строк.

Например:

SELECT * FROM Phones WHERE Mobile = convert(nvarchar,@biMobile_2)

Надеюсь, это поможет.

0 голосов
/ 06 мая 2009

Вы прекрасно описываете, что происходит, поэтому я не понимаю, что вы хотели бы знать, чего вы еще не знаете?

Исправить запрос достаточно просто, просто попросите Sql Server привести ваш параметр к varchar вместо преобразования столбца varchar в bigint:

SELECT * FROM Phones WHERE Mobile = cast(@biMobile_1 as varchar(50))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...