跨列/不同行过滤值列表的高效查询

时间:2021-06-26 03:31:23

标签: sql-server tsql

SQL Server 版本为 2016+/Azure SqlDb(如果添加则灵活,两者兼容,向前兼容)。

用例是 API 用户发送单列值列表来过滤某些目标表。目标表有 2-n 列,其值在被查询的表/范围的行(可能是列,无关紧要)中是唯一的。 (到目前为止n <= 5,但这是一个细节/不能保证。)

这是一个足够好的示例表 DDL:

IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'SomeTable')
BEGIN
    CREATE TABLE dbo.SomeTable (
        ID int IDENTITY(1, 1) not null PRIMARY KEY CLUSTERED
      , NaturalKey1 nvarchar(10) not null UNIQUE NONCLUSTERED
      , NaturalKey2 nvarchar(10) not null UNIQUE NONCLUSTERED
      , NaturalKey3 nvarchar(10) not null UNIQUE NONCLUSTERED
        );
END

IF NOT EXISTS (SELECT 1 FROM dbo.SomeTable)
BEGIN
    INSERT INTO dbo.SomeTable VALUES
        ('A', 'AA', 'ZZZZZ')
       ,('B', 'B', 'YYYYY')
       ,('C', 'CC', 'XXX')
       ,('D', 'DDD', 'WWWWW')
       ,('E', 'EEEE', 'V')
       ,('F', 'FF', 'UUUUUUUUU')
       ,('G', 'GGGGGGGG', 's')
       -- lots more
        ;
END

SELECT * FROM dbo.SomeTable;

-- DROP TABLE dbo.SomeTable;

假设所有 NaturalKey 列都是相同类型(可能是 nvarchar);过滤发生在数据库端;并在存储过程中以尽可能少的步骤执行,最好是一次执行。参数将是字符串列表或 TVP,实际上并不重要。结果将包括 SomeTable 的任何行中与任何列上的任何值匹配的所有数据。目标表的大小未知。

这是我们上面朋友的示例参数:

DECLARE @filterValues nvarchar(1000) = 'DDD,XXX,E,HH,ok,whatever,YYYYY';
SELECT * FROM string_split(@filterValues, ',');

我知道几种方法可以做到这一点,并且可以想象更多的方法,所以它不会被卡住。我敢打赌,有人知道比我将要说明的两个技巧中的任何一个都更好的技巧。

方法 1 构建一个临时表更新并加入它(简洁且易于审计,这对专业人士来说是这样)

DECLARE @filterValues nvarchar(1000) = 'DDD,XXX,E,HH,ok,whatever,YYYYY';

SELECT value AS InValue, CONVERT(int, null) AS IDMatch
INTO #filters
FROM string_split(@filterValues, ',');

UPDATE f
SET f.IDMatch = st.ID
FROM #filters AS f
    INNER JOIN dbo.SomeTable AS st ON f.InValue IN (st.NaturalKey1, st.NaturalKey2, st.NaturalKey3);

SELECT * FROM #filters; -- Audit

SELECT st.* FROM #filters AS f INNER JOIN dbo.SomeTable AS st ON f.IDMatch = st.ID;

IF OBJECT_ID('tempdb..#filters') IS NOT NULL DROP TABLE #filters;

方法 2 取消旋转 SomeTable(我喜欢漂亮的 cross apply trick)并加入(大规模地有我认为的食人魔)

SELECT 
        st.*
FROM 
    dbo.SomeTable AS st
    CROSS APPLY (VALUES (st.NaturalKey1)
                      , (st.NaturalKey2)
                      , (st.NaturalKey3)
                ) AS nk(Val)
    INNER JOIN #filters AS f ON nk.Val = f.InValue;

IF OBJECT_ID('tempdb..#filters') IS NOT NULL DROP TABLE #filters;

我们的未来有问题吗

有效总比无效要好,但要从 T-SQL 专家那里寻找更好/更有效/更具可扩展性的方法。列和行中的未知维度,响应时间是 SLA,过滤器大小可能有上限,也可能没有上限。如果这整齐地从 SomeTable 移植到 SomeTableVersionN,则加分。 (API 中没有动态 SQL。)

可能是骗人的问题,找不到,指出就好了,谢谢。

0 个答案:

没有答案
相关问题