IamKarlson
@IamKarlson
ASP(?).NET, SQL-разработчик

Выполнение CASE

Есть колонка в которой есть произвольное количество строк в которой могут быть либо числа, либо строки, и нужно отобрать только те у которых эта колонка заполнена числом и вернуть их в инт. Пишем запрос такой:

SELECT
Id
,stringwithnumeric
WHERE
CASE
WHEN ISNUMERIC (stringwithnumeric)=1
THEN CAST (stringwithnumeric AS INT)
ELSE 0
END > 0


Вываливается ошибка, что варчар в инт каст мы не можем. Ну хорошо, если у нас when для кейса не проходит, то then по нормальной человеческой логике не выполнится, так думал я. Оказывается, что case проходит выполнение всех веток. Каким образом это работает эффективнее чем отброс по условию, и после применение then? Чем можно объяснить такую странную логику выполнения?
  • Вопрос задан
  • 3693 просмотра
Решения вопроса 1
@lair
Это объясняется тем, что у вас CASE внутри WHERE, и оптимизитор считает, что так будет эффективнее.
Ответ написан
Пригласить эксперта
Ответы на вопрос 4
Antelle
@Antelle
А может, там где есть «0.1» или даже просто «.»? Оно isnumeric, но не int.
Ответ написан
@GalinaM
Вот таблица с тестовыми данными

CREATE TABLE [dbo].[TableTest](
[Id] [int] IDENTITY(1,1) NOT NULL,
[stringwithnumeric] [varchar](50) NULL,
CONSTRAINT [PK_TableTest] 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
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[TableTest] ON
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (1, N'1')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (2, N'dsa')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (3, N'3')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (4, N'3')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (5, N'gg')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (6, N'del')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (7, N'33')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (8, N'67')
INSERT [dbo].[TableTest] ([Id], [stringwithnumeric]) VALUES (9, N'97')
SET IDENTITY_INSERT [dbo].[TableTest] OFF

Вот запрос

SELECT
Id
,stringwithnumeric
FROM dbo.TableTest
WHERE ISNUMERIC (stringwithnumeric)=1

Или что-то другое хотелось?
Ответ написан
SatansClaws
@SatansClaws
по табличке GalinaM ваш запрос прекрасно выполняется

Но вот если воткнуть в данные строку вида '1.1' — то ваш запрос ломается,
а вот такое условие — работает:
WHERE 
	CASE 
		--WHEN ISNUMERIC (stringwithnumeric)=1 THEN CAST (stringwithnumeric AS INT) 
		WHEN ISNUMERIC (stringwithnumeric)=1 THEN convert(int, convert(numeric(19, 3), stringwithnumeric))
		ELSE 0 
	END > 0


ЗЫ отучайтесть использовать CAST — он оставлен для обратной совместимости. Используйте CONVERT
Ответ написан
SatansClaws
@SatansClaws
дык, проверьте данные в таблице
я на 80% уверен, что в ней есть строки вида "[число].[число]" или близкое к такому
короче, для которого IsNumeric() вернет 1, но приведение типа не сработает

кстати, мой вариант ломается при наличии в данных значения '.'

ЗЫ каст плох тем, что
а) конвертить дату из строки или наоборот выходит ненадежно
б) он таки устаревший. А значит рано или поздно его в скуле не станет (и есть ли он в MS SQL 2012?).
А поскольку какие-то удачные куски кода имеют свойство мигрировать из проекта в проект — может оказаться, что он где-то всплывет, даже если СУБД этого проекта вы апдейтить не будете.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы