SQL Сервер 2017 оценил фрагмент кода, который никогда не должен оцениваться - PullRequest
0 голосов
/ 03 августа 2020
select * 
from fb_lab_test 
where (report_item_code = 'HBcAb') 
   or (report_item_code = 'Anti-Hbc' and 
       case isnumeric(result) when 1 then cast(result as float) else 10000.0 end > 0.2)

Ошибка преобразования типа данных varchar в float

Вот пример данных

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE fb_lab_test
(
    [id] [numeric](18, 0) IDENTITY(1,1) NOT NULL,
    [Test_No] [varchar](50) NULL,
    [execute_date] [datetime] NULL,
    [PatientId] [varchar](20) NULL,
    [Visit_Id] [varchar](10) NULL,
    [Patient_Type] [int] NULL,
    [PatientName] [varchar](100) NULL,
    [result_date_time] [datetime] NULL,
    [report_item_name] [varchar](256) NULL,
    [report_item_code] [varchar](50) NULL,
    [result] [varchar](100) NULL
) ON [PRIMARY]
GO

INSERT INTO fb_lab_test 
VALUES ('5910315197','2019-10-31 00:40:53.000','111111','1','1','Tom','2019-10-31 08:56:54.000','test1','KET','-')

В этом примере данных isnumeric вернет ложные срабатывания , но case isnumeric(result) when 1 then cast(result as float) else 10000.0 end > 0.2 никогда не следует оценивать, потому что в выборке даты нет report_item_code с именем 'Anti-Hb c', что странно.

Ответы [ 3 ]

1 голос
/ 03 августа 2020

В моем случае (с использованием ваших данных) ошибка не выдается. Иногда движок решает выполнить операции заранее (чтобы оптимизировать тот или иной запрос). Я полагаю, вы получаете сообщение об ошибке из-за такого поведения.

Вы можете исправить это, используя TRY_CAST:

select * from fb_lab_test 
where (report_item_code = 'HBcAb') 
or (
    report_item_code = 'Anti-Hbc' and 
    case isnumeric(result) when 1 then try_cast(result as float) else 10000.0 end > 0.2
)
0 голосов
/ 03 августа 2020

Не используйте isnumeric() вообще. Просто используйте функции try_:

select t.* 
from fb_lab_test t
where report_item_code = 'HBcAb'  or
      (report_item_code = 'Anti-Hbc' and 
       coalesce(try_cast(float_result), 10000.0) > 0.2
      );

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

В этом случае вы хотите включить нечисловые c значения. Чаще они исключаются. Вы можете просто позволить сравнению NULL исключить их. Это logi c будет:

select t.* 
from fb_lab_test t
where report_item_code = 'HBcAb'  or
      (report_item_code = 'Anti-Hbc' and 
       try_cast(float_result) > 0.2   -- excludes non-NULL values
      );
0 голосов
/ 03 августа 2020

На самом деле это соответствует документации ...

ISNUMERI C возвращает 1 для некоторых символов, которые не являются числами, таких как плюс (+), минус (-), и допустимые символы валюты, такие как как знак доллара ($). Полный список символов валют см. В разделе money и smallmoney (Transact- SQL).

https://docs.microsoft.com/en-us/sql/t-sql/functions/isnumeric-transact-sql?view=sql-server-ver15

SQL Сервер возвращает сообщение об ошибке, когда преобразование данных nonnumeri c char, nchar, nvarchar или varchar в decimal, float, int, numeri c. SQL Сервер также возвращает ошибку, когда пустая строка ("") преобразована в число c или десятичное.

https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver15

Если вам нужно чтобы преобразовать его в одно выражение, я предлагаю вам использовать тип денег, который поддерживает до 4 знаков после запятой. В противном случае вам придется иметь дело с «возможными» числовыми значениями + - . et c ...

...