根据输入(表)数据构建S​​QL'where'子句

时间:2019-02-08 11:24:40

标签: sql tsql

简介

我正在创建一个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#将子句解析为字符串,但是有点需要保持这种简单!

非常感谢。

3 个答案:

答案 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

感谢您的所有评论和答案。