在SQL中匹配多个键/值对

时间:2016-03-09 09:45:00

标签: sql sql-server

我将元数据存储在SQL Server的键/值表中。 (我知道键/值很糟糕,但这是用户提供的自由格式元数据,因此我无法将键转换为列。)用户需要能够为我提供一组任意键/值对并且具有我将返回符合所有这些条件的所有数据库对象。

例如:

Metadata:
Id Key Value
1  a   p
1  b   q
1  c   r
2  a   p
2  b   p
3  c   r

如果用户说a = p且b = q,我应该返回对象1.(不是对象2,即使它也有= p,因为它有b = p。)

要匹配的元数据位于具有简单键/值架构的表值sproc参数中。我最接近的是:

select * from [Objects] as o
where not exists (
  select * from [Metadata] as m
  join @data as n on (n.[Key] = m.[Key])
  and n.[Value] != m.[Value]
  and m.[Id] = o.[Id]
)

我的“不存在不匹配的行”是通过形成其对立来实现“所有行匹配”的尝试。这确实消除了元数据不匹配的对象,但它也返回没有元数据的对象,所以没有好处。

有人能指出我正确的方向吗? (表现和正确性的奖励点。)

2 个答案:

答案 0 :(得分:0)

; WITH Metadata (Id, [Key], Value) AS  -- Create sample data
(
    SELECT 1,  'a',   'p' UNION ALL
    SELECT 1,  'b',   'q' UNION ALL
    SELECT 1,  'c',   'r' UNION ALL
    SELECT 2,  'a',   'p' UNION ALL
    SELECT 2,  'b',   'p' UNION ALL
    SELECT 3,  'c',   'r' 
),
data ([Key], Value) AS  -- sample input
(
    SELECT 'a', 'p' UNION ALL
    SELECT 'b', 'q'
),
-- here onwards is the actual query
data2 AS
(
    -- cnt is to count no of input rows
    SELECT [Key], Value, cnt = COUNT(*) OVER() 
    FROM data
)
SELECT m.Id
FROM Metadata m
INNER JOIN data2 d ON m.[Key] = d.[Key] AND m.Value= d.Value
GROUP BY m.Id
HAVING COUNT(*) = MAX(d.cnt)

答案 1 :(得分:0)

以下SQL查询会生成您需要的结果。

SELECT *
FROM @Objects m
WHERE Id IN
(
    -- Include objects that match the conditions:
    SELECT m.Id
    FROM @Metadata m
    JOIN @data d ON m.[Key] = d.[Key] AND m.Value = d.Value

    -- And discount those where there is other metadata not matching the conditions:
    EXCEPT
    SELECT m.Id
    FROM @Metadata m
    JOIN @data d ON m.[Key] = d.[Key] AND m.Value <> d.Value
)

测试我使用的架构和数据:

-- Schema
DECLARE @Objects TABLE (Id int);
DECLARE @Metadata TABLE (Id int, [Key] char(1), Value char(2));
DECLARE @data TABLE ([Key] char(1), Value char(1));

-- Data
INSERT INTO @Metadata VALUES
  (1,  'a',   'p'),
  (1,  'b',   'q'),
  (1,  'c',   'r'),
  (2,  'a',   'p'),
  (2,  'b',   'p'),
  (3,  'c',   'r');

INSERT INTO @Objects VALUES
  (1),
  (2),
  (3),
  (4);  -- Object with no metadata

INSERT INTO @data VALUES
  ('a','p'),
  ('b','q');