获取该组值的匹配项

时间:2016-05-25 21:32:37

标签: sql sql-server database

让我们想象一下我有两张桌子

**T1**
T1ID
----
1
2
3

**T2**
T2ID| T1ID| VALUE|
------------------
   1|    1|     1|
   2|    1|     2|
   3|    2|     1|
   4|    2|     3|
   5|    2|     2|
   6|    3|     1|
   7|    3|     4|

T1是T2中值的分组表 因此,我有三组价值观

1 = 1, 2
2 = 1, 2, 3
3 = 1, 4

是否可以编写查询来获取与这组值匹配的所有组?

SELECT … WHERE VALUE IN (1, 2, 3)
SHOULD GIVE AS RESULT 
1
2

SELECT … WHERE VALUE IN (1, 4)
SHOULD GIVE AS RESULT 
3

下面的答案之一指出我要以适当的方式挖掘:

SELECT s.t1id
  FROM (SELECT t1id, COUNT(value) itemCount
          FROM t2  
         WHERE VALUE IN (1, 2, 3) -- put set of values here
         GROUP BY t1id) s
  JOIN (SELECT t1id, COUNT(VALUE) itemCount
          FROM t2
         GROUP BY t1id) j 
    ON s.t1id = j.t1id 
 WHERE s.itemCount >= j.itemCount

有人可以改进查询吗?

2 个答案:

答案 0 :(得分:0)

您可以通过多种方式实现这一目标。相反的测试("不在列表中#34;)可以产生有趣的解决方案:

 SELECT    t1id
 FROM      t2
 GROUP BY  t1id
 HAVING    COUNT(CASE WHEN value NOT IN (1,4) THEN 1 END) = 0

上述查询不包括表 t2 中根本没有值的那些键。严格来说,空集是任何其他集的子集,因此实际上也应列出这些键。使用外连接可以实现,如下所示:

 SELECT    t1.t1id
 FROM      t1
 LEFT JOIN t2
        ON t1.t1id = t2.t1id
 GROUP BY  t1.t1id
 HAVING    COUNT(CASE WHEN value NOT IN (1,4) THEN 1 END) = 0

或者,将IN测试作为外连接条件:

 SELECT    t1.t1id
 FROM      t1
 LEFT JOIN t2
        ON t1.t1id = t2.t1id
       AND value NOT IN (1,4)
 WHERE     t2.t1id IS NULL
 GROUP BY  t1.t1id

或者,使用子查询:

 SELECT   t1id
 FROM     t1
 WHERE    t1id NOT IN (SELECT t1id 
                       FROM   t2
                       WHERE  value NOT IN (1,4))

您也可以使用MINUS,这非常直观:

 SELECT   t1id
 FROM     t1
 MINUS
 SELECT   t1id
 FROM     t2
 WHERE    t1id NOT IN (1,4)

快速查看您的案例中表现最佳的内容。

答案 1 :(得分:0)

我写了以下内容......不确定您是否使用Oracle,但这似乎有效。如果这对您更有效,请告诉我。

select *
from
(
    select  
    t1_id,
    LISTAGG(value, '') WITHIN GROUP (ORDER BY value) value_list
    from
    (
        select 1 t1_id, 1 value from dual union all
        select 1 t1_id, 2 value from dual union all
        select 2 t1_id, 1 value from dual union all
        select 2 t1_id, 3 value from dual union all
        select 2 t1_id, 2 value from dual union all
        select 3 t1_id, 1 value from dual union all
        select 3 t1_id, 4 value from dual
    )
    t2
    group by t1_id
)
where
instr('123',value_list) > 0
编辑:我注意到SQLServer标签......刚才。对于那个很抱歉。所以我用SQLServer语法重写了我的解决方案。

其他编辑:对,你是......我认为以下将纠正这种行为。但是,我不能评论下面方法的效率。我认为LISTAGG在Oracle中的内联功能可能更有效 - 只是因为比我聪明的人更优化了它。

with t2 as
(
    select 1 t2_id, 1 t1_id, 1 value union all
    select 2 t2_id, 1 t1_id, 2 value union all
    select 3 t2_id, 2 t1_id, 1 value union all
    select 4 t2_id, 2 t1_id, 3 value union all
    select 5 t2_id, 2 t1_id, 2 value union all
    select 6 t2_id, 3 t1_id, 1 value union all
    select 7 t2_id, 3 t1_id, 4 value
)
SELECT
*
FROM
(
    SELECT DISTINCT
    b.t1_id,
    STUFF(CAST((
            SELECT [text()] = '' + cast(a.Value AS VARCHAR(100))
            FROM t2 a
            WHERE a.t1_id = b.t1_id
            ORDER BY a.Value
            FOR XML PATH(''), TYPE) AS VARCHAR(1000)), 1, 0, ''
    ) value_list
    from
    t2 b
) x
where
CHARINDEX(value_list,'123',1) > 0 OR
CHARINDEX('123',value_list,1) > 0

最终编辑:对不起,我已经在评论中做了这些,如果我没有被认为太新用户被允许发表评论。我认为你是对的。我没有完全理解这些要求,但我已经开始了。如果我们在t2中添加一个值(1,3),我的过程将再次在搜索' 123'时失败。我认为我目前的解决方案是错误的,但我认为可能有一些优点可以进行解包和排序,以实现更有效的模式匹配。但是,它可能会取消它的LISTAGG部分。但是,既然你已经有了一个有效的解决方案,那么我最好退出并对更有成效的问题进行痴迷。

抱歉,我没有帮助,哈哈。先生,祝你好运。