Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Хитрый запрос  [new]
remi_
Member

Откуда: СПб
Сообщений: 248
Есть таблица, в которой расписаны формальные условия. Примерно так
declare @Tab table (IntId int, V1 nvarchar(4000), V2 nvarchar(4000), Operation varchar(4), DataType varchar(128))

insert into @Tab(IntId, V1, V2, Operation, DataType) select
1, '20', '1', '>', 'int' union all select
2, 'ABC', 'D', '<=', 'varchar(10)' union all select
3, '1.1', '1.1', '=', 'money' union all select
4, 'A20-B40', '%20%', 'LIKE', 'nvarchar(1000)' 


Надо выбрать те строки для которых выполняется условие.
Условие собирается так: V1 преобразованное по DataType Operation V2 преобразованное по DataType. Т.е., например, для последней строки условие будет
cast('A20-B40' as nvarchar(1000)) LIKE cast('%20%' as nvarchar(1000))

В качестве Operation могут быть использованы: =, =>, <=, >, <, LIKE
В качестве DataType T-SQL типы данных.

По примеру в результате должны быть выбраны строки с IntId in (2, 3, 4).

Как делать через курсор понятно. Нельзя ли это реализовать одним запросом?
21 мар 13, 21:27    [14079375]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
invm
Member

Откуда: Москва
Сообщений: 9826
Одним запросом можно будет, если напишите для вычисления предиката CLR-функцию. Но без курсора можно.
declare @Tab table (IntId int, V1 nvarchar(4000), V2 nvarchar(4000), Operation varchar(4), DataType varchar(128));

insert into @Tab(IntId, V1, V2, Operation, DataType) select
1, '20', '1', '>', 'int' union all select
2, '''ABC''', '''D''', '<=', 'varchar(10)' union all select
3, '1.1', '1.1', '=', 'money' union all select
4, '''A20-B40''', '''%20%''', 'LIKE', 'nvarchar(1000)';

declare @s varchar(max);

select @s = stuff(
 (select
  ' union all select ' + cast(IntId as varchar(10)) + ' where cast(' + V1 + ' as ' + DataType + ') ' + Operation + ' cast(' + V2 + ' as ' + DataType + ')'
 from
  @Tab
 for
  xml path(''), type).value('.', 'varchar(max)'), 1, 11, '');

declare @t table (IntId int);

insert into @t
 exec(@s);
 
select
 t2.*
from
 @t t1 join
 @Tab t2 on t2.IntId = t1.IntId;
Если еще не поздно, откажитесь от этой жути.
21 мар 13, 22:50    [14079531]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
remi_
Member

Откуда: СПб
Сообщений: 248
С одной стороны Вы правы. Только немного упростили задачу, поставив нужные кавычки.
В таблице @Tab никаких лишних кавычек изначально нет, а анализировать тип данных и добавлять их для datetime, smalldatetime, char, varchar и т.д. совсем уж неуместно.
Согласен, жуть, но ведь и интересно. Такова уж система ERP - все описывается в максимально обобщенном виде, а расхлебывать приходится на нижнем уровне.
22 мар 13, 11:20    [14080908]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
invm
Member

Откуда: Москва
Сообщений: 9826
remi_
В таблице @Tab никаких лишних кавычек изначально нет, а анализировать тип данных и добавлять их для datetime, smalldatetime, char, varchar и т.д. совсем уж неуместно.
И как вы, не добавляя кавычек, собирались формировать синтаксически правильные выражения?
22 мар 13, 11:49    [14081108]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
remi_
Member

Откуда: СПб
Сообщений: 248
Ну, например

declare @Tab table (IntId int, V1 nvarchar(4000), V2 nvarchar(4000), Operation varchar(4), DataType varchar(128))

insert into @Tab(IntId, V1, V2, Operation, DataType) select
1, '20', '1', '<', 'int' union all select
2, 'ABC', 'D', '<=', 'varchar(10)' union all select
3, '1.1', '1.1', '=', 'money' union all select
4, 'A20-B40', '%20%', 'LIKE', 'nvarchar(1000)' 

declare @Stri nvarchar(4000), @IntId int, @V1 nvarchar(4000), 
	@Operation varchar(4), @V2 nvarchar(4000), @DataType varchar(128), @Res tinyint

DECLARE CurB CURSOR local static forward_only
FOR SELECT IntId, V1, Operation, V2, DataType from @Tab

OPEN CurB

FETCH NEXT FROM CurB INTO @IntId, @V1, @Operation, @V2, @DataType
WHILE (@@fetch_status <> -1)
BEGIN
	IF (@@fetch_status <> -2)
	BEGIN
		set @Stri=N'
		declare @SV1 '+@DataType+', @SV2 '+@DataType+'
		select @SV1=cast(@V1 as '+@DataType+'), @SV2=cast(@V2 as '+@DataType+')
		set @Res=case when @SV1 '+@Operation+' @SV2 then 1 else 0 end'
		
		exec sp_executesql @Stri, N'@Res tinyint out, @V1 nvarchar(4000), @V2 nvarchar(4000)',@Res out, @V1, @V2
		
		if @Res=0 delete from @Tab where IntId=@IntId
	END
	FETCH NEXT FROM CurB INTO @IntId, @V1, @Operation, @V2, @DataType
END

CLOSE CurB
DEALLOCATE CurB

select * from @Tab
22 мар 13, 11:57    [14081193]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
invm
Member

Откуда: Москва
Сообщений: 9826
remi_,

Вы серьезно считаете это более уместным?
22 мар 13, 12:16    [14081371]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
remi_
Member

Откуда: СПб
Сообщений: 248
Никоим образом, иначе бы не открывал тему.
Просто этот код работает.
22 мар 13, 12:17    [14081380]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
Cygapb-007
Member

Откуда:
Сообщений: 1677
invm, на самом деле в данных апострофы не обязательны, достаточно
+ ' where cast(''' + V1 + ''' as ' + DataType + ') '+ 
22 мар 13, 12:22    [14081432]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
invm
Member

Откуда: Москва
Сообщений: 9826
Cygapb-007,

select
 cast(0x01 as varbinary(10)), cast('0x01' as varbinary(10));
22 мар 13, 12:40    [14081563]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
remi_
Member

Откуда: СПб
Сообщений: 248
to Cygapb-007
А ведь и впрямь!
22 мар 13, 13:05    [14081793]     Ответить | Цитировать Сообщить модератору
 Re: Хитрый запрос  [new]
remi_
Member

Откуда: СПб
Сообщений: 248
Тут коллега предложил весьма убедительное решение

if OBJECT_ID('tempdb..#Tab') IS NOT NULL drop table #Tab

create table #Tab (IntId int, V1 nvarchar(4000), V2 nvarchar(4000), Operation varchar(4), DataType varchar(128))
insert into #Tab(IntId, V1, V2, Operation, DataType) select
1, '20', '1', '<', 'int' union all select
2, 'ABC', 'D', '<=', 'varchar(10)' union all select
3, '1.1', '1.1', '=', 'money' union all select
4, 'A20-B40', '%20%', 'LIKE', 'nvarchar(1000)' 

declare @s varchar(8000) = ''
select @s = @s + 'select * from (
	select * from #Tab 
	where Operation = ''' + Operation + ''' and DataType = ''' + DataType + '''
	) a 
where cast(V1 as ' + DataType + ') ' + Operation + ' cast(V2 as ' + DataType + ') union all ' + CHAR(13)
from (select distinct Operation, DataType from #Tab) a
select @s = SUBSTRING(@s, 1, datalength(@s) - 11)

print @s
exec(@s)
22 мар 13, 14:34    [14082573]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить