简介
我正在创建一个SQL Server存储过程,以从大型表中动态获取数据。有很多(30+)列,可能/可能不需要将所选数据返回。
我有一个动态SQL选择查询,它使用一个无透视图(因此列被列为行),按预期方式工作...现在,我需要过滤这些结果。
输入表:
Column
A
B
表格:
A B C
---------------------------
value1 valueZ
value2 valueY
value3 valueX
value0 valueA
value4
valueX
预期结果:
Col Value Count
-------------------
A value1 1
A value2 1
A value3 1
A value0 1
A value4 1
B valueZ 1
B valueY 1
B valueX 2
(由于没有输入列选择,所以没有C)
问题
作为输入,我有一个表类型,列出了需要返回的列。我打算将其扩展为包括用于过滤给定列的值。
示例表类型输入:
Column Value
-----------------
A value1
A value2
A value3
B valueX
B valueY
由此,我需要构建一个类似where的子句。相同的列进行或运算;当列更改时,它是ANDed。
WHERE (A = 'value1'
OR A = 'value2'
OR A = 'value3')
AND (B = 'valueX' OR B = 'valueY')
到目前为止,我已经有了一个COALESCE,但不知道如何确定列的变化-我真的不想要光标。
作为C#开发人员-SQL可以为我提供任何“魔术”循环来实现这一目标吗?
我还可以使用C#将子句解析为字符串,但是有点需要保持这种简单!
非常感谢。
答案 0 :(得分:1)
如果我正确理解了您的情况,您已经拥有所需的unpivot
ed输出,您只需要从一组给定的值中进行过滤?
在这种情况下,您可以将这些值放入派生表中,并通过inner join
过滤第一个数据集:
declare @t table(Col varchar(1),Val varchar(6),Cnt int);
insert into @t values('A','value1',1),('A','value2',1),('A','value3',1),('A','value0',1),('A','value4',1),('B','valueZ',1),('B','valueY',1),('B','valueX',2);
declare @f table(Col varchar(1),Val varchar(6));
insert into @f values('A','value1'),('A','value2'),('A','value3'),('B','valueX'),('B','valueY');
select t.*
from @t as t
join @f as f
on t.Col = f.Col
and t.Val = f.Val;
+-----+--------+-----+
| Col | Val | Cnt |
+-----+--------+-----+
| A | value1 | 1 |
| A | value2 | 1 |
| A | value3 | 1 |
| B | valueY | 1 |
| B | valueX | 2 |
+-----+--------+-----+
答案 1 :(得分:1)
您可以尝试一下。首先使用下面的脚本创建临时表。 COLUNM_A是您的列,VALUE_A是您的值列。
SELECT ROW_NUMBER() OVER (ORDER BY A.COLUMN_A) AS ID, A.COLUMN_A, A.VALUE_A,
CASE WHEN LEAD(A.COLUMN_A,1) OVER (ORDER BY A.COLUMN_A) = A.COLUMN_A THEN 1 ELSE 0 END AS IDEN
INTO #T1
FROM dbo.YOUR_TABLE A
临时表如下所示,您有一个新的IDEN列,用于定义“ OR”和“ AND”陈述式
SELECT * FROM #T1
之后,您可以尝试以下操作:
DECLARE @SQL_WHERE NVARCHAR(1500),
@val1 INT,
@valmax INT
SET @valmax = (SELECT COUNT(*) FROM #T1)
SET @val1 = 1
SET @SQL_WHERE = '('
WHILE @val1 <= @valmax
BEGIN
SET @SQL_WHERE = @SQL_WHERE + (SELECT COLUMN_A + ' = ''' + VALUE_A + CASE WHEN IDEN = 1 THEN ''' OR ' ELSE ''') AND (' END
FROM #T1 WHERE ID = @val1)
SET @val1 = @val1 + 1
END
SELECT 'WHERE ' + SUBSTRING(@SQL_WHERE, 1 , LEN(@SQL_WHERE) - 5)
最后一次选择将返回正确的WHERE子句
答案 2 :(得分:0)
最感谢@ user2114537,我想到了这个(这不漂亮!)。我确实需要在数据透视表之前应用动态过滤器。
我们正在使用SQL Server 2008,因此LEAD()不可用,我也使用了COALESCE而不是@ user2114537的WHILE(请查看他们的答案https://stackoverflow.com/a/54592090/4616330,对于SQLServer> = 2012)。
-- create a tmp table var for and/or where data
DECLARE @t TABLE([Column] nvarchar(50), [Value] nvarchar(50), [andOr] nvarchar(7))
-- create a table with row numbers (for joining to self)
;WITH cte AS
(SELECT f.[Column], f.[Value], ROW_NUMBER() OVER (ORDER BY f.[Column], f.[Value]) AS [RowNo]
FROM @InputTable f
)
-- join table to itself to reproduce 2012's LEAD function and insert AND/OR based on col being same as previous
INSERT INTO @t([Column], [Value], [andOr])
SELECT c1.[Column], c1.[Value], CASE WHEN c1.[Column] = c2.[Column] THEN ') AND (' ELSE ' OR ' END AS [andOr]
FROM cte c1 LEFT OUTER JOIN cte c2 ON c1.[RowNo] = c2.[RowNo] + 1
-- build a where clause from andOr info
DECLARE @where AS NVARCHAR(MAX),
@whereBuilt AS NVARCHAR(MAX)
SELECT @where = COALESCE(@where, '') + (QUOTENAME([Column]) + '=''' + [Value] + ''' ' + [andOr] + ' ')
FROM @t
-- fiddle to get a valid where...
SET @whereBuilt = '(' + SUBSTRING(@where, 1, LEN(@where) - 4) + ')'
PRINT @whereBuilt
感谢您的所有评论和答案。