【问题标题】:How to select a primary key which has exact foreign keys matches a given list of values?如何选择具有与给定值列表完全匹配的外键的主键?
【发布时间】:2017-05-10 15:07:19
【问题描述】:

例如:

pk_ref    fk
======    ===
1         a
1         b
1         c
2         a
2         b
2         d

如何进行类似“伪”查询的查询:

select distinc pk_ref
where fk in all('a', 'c');

返回的查询结果必须匹配列表中外键的所有给定值。

结果应该是:

1

虽然下面的选择不能返回任何记录。

select distinc pk_ref
where fk in all('a', 'c', 'd');

我该怎么做?

【问题讨论】:

  • 你在使用 SQL Server 吗?
  • 是的,但是如果有标准的sql不管db引擎会更好。
  • fk值的“变量”列表是如何传入的?一种通用方法可能是有一个子查询,您可以在其中选择不同的 pk,fk where fk in list,并在外部查询中按 pk 分组并计算行数。但是对于最终过滤器(HAVING 子句),您需要知道列表中有多少个值,因此您需要能够计算它们。那么......如何将列表提供给查询?
  • @mathguy fk 值列表将由用户在应用程序中以字符串形式给出,那么预期的查询结果应该如上面所讨论的那样。

标签: mysql sql sql-server oracle


【解决方案1】:

试试这个

select pk_ref 
from yourtable 
group by pk_ref 
having count(case when fk = 'a',  then 1 end) >= 1 
and count(case when fk = 'c' then 1 end) >= 1

动态执行。 (考虑到您使用的是 SQL SERVER)

创建一个拆分字符串函数并将输入作为逗号分隔值传递

Declare @input varchar(8000)= 'a,c',@cnt int 

set @cnt = len(@input)-len(replace(@input,',','')) + 1

select pk_ref 
from yourtable 
Where fk in (select split_values from udf_splitstring(@input , ','))
group by pk_ref 
having count(Distinct fk) >= @cnt 

您可以从下面的链接创建一个拆分字符串函数

https://sqlperformance.com/2012/07/t-sql-queries/split-strings

【讨论】:

  • 我有未知数量的值,值列表是变体。
  • @DevX - 那么你需要使用动态查询
  • 怎么做?
  • 不需要动态查询。复杂而低效。输入字符串也不需要拆分。
  • @mathguy 它比Like 复杂但效率更高。假设您在 fk 列上有 1000 万条带有索引的记录。我的会比你的表现好得多..
【解决方案2】:

:list 是输入列表(绑定变量)。 length() 返回值的区别在于绑定变量中逗号的数量。这个查询,或者非常接近它的查询,应该可以在几乎任何数据库产品中使用。在 Oracle 中测试。

select   pk_ref
from     tbl    --  enter your table name here
where    ',' || :list || ','   like   '%,' || fk || ',%'
group by pk_ref
having   count(distinct fk) = 1 + length(:list) - length(replace(:list, ',', ''))

【讨论】:

  • 我认为这根本没有效率。它看起来很简单。在 Like 中前导 % 将使查询不可搜索。
【解决方案3】:

如果您可以将 IN 运算符值作为 Set 传递,那么您可以按如下方式执行此操作

架构:

SELECT * INTO #TAB FROM (
SELECT 1 ID, 'a' FK
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 1, 'c'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
UNION ALL
SELECT 2, 'd'
UNION ALL
SELECT 1, 'a'
)AS A

使用 CTE 将 'a','c' 设为 Set

;WITH CTE AS (
SELECT 'a' FK   --Here 'a','c' passed as a Set through CTE
UNION 
SELECT 'c'
)
,FINAL AS(

SELECT DENSE_RANK() OVER (PARTITION BY ID ORDER BY (FK))AS COUNT_ID, ID, FK 
FROM #TAB where FK  IN (select FK FROM CTE)

)
SELECT ID FROM FINAL WHERE COUNT_ID>=(SELECT COUNT( FK) FROM CTE)

【讨论】:

    【解决方案4】:
    Select pk_ref where fk='a' and pk_ref in (select pk_ref where fk='c' from yourtable) from yourtable;  
    

    select pk_ref where fk='a' from yourtable intersect select pk_ref where fk='c' from yourtable;
    

    【讨论】:

    • 相交查询是一个完美的答案,但是如何将其作为一个动态查询,所以可以将任意数量的值传递给它,而不仅仅是像 'a' 和 'c' 这样的两个变量?
    【解决方案5】:
        DECLARE @inputVariable VARCHAR(200) =  'a,b,c,d'
        DECLARE @inputValue INT
        DECLARE @tblInput TABLE
        (
            FK VARCHAR(100)
        )
    
    
    
    
        INSERT INTO @tblInput
        SELECT SUBSTRING( @inputVariable+',',RN,1)
          FROM (SELECT TOP 100 ROW_NUMBER() OVER(ORDER BY s.object_id) RN
          FROM sys.objects s)  s
          where LEN(@inputVariable) >= RN
          AND SUBSTRING(','+ @inputVariable,RN,1) = ','
    
        SELECT @inputValue = COUNT(1) FROm @tblInput
    
        --@inputVariable
    
        DECLARE @tbl TABLE 
        (
            ID INT,
            FK VARCHAR(100) 
        )
    
        INSERT INTO @tbl
        SELECT 1 ID, 'a' FK
        UNION ALL
        SELECT 1, 'b'
        UNION ALL
        SELECT 1, 'c'
        UNION ALL
        SELECT 2, 'a'
        UNION ALL
        SELECT 2, 'b'
        UNION ALL
        SELECT 2, 'd'
        UNION ALL
        SELECT 1, 'a'
    
        SELECT t.ID ,COUNT(DISTINCT t.FK) 
          FROM @tbl t
        INNER JOIn @tblInput  ti
                ON t.FK = ti.FK
            GROUP BY ID
            HAVING COUNT(DISTINCT t.FK) = @inputValue
    

    【讨论】:

      猜你喜欢
      • 2023-01-10
      • 1970-01-01
      • 2021-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-26
      • 1970-01-01
      相关资源
      最近更新 更多