3

我有以下两张表,一张是人表,另一张表是用于存储有关该人的各种动态属性/信息。

 Id | Persons               PersonId | Field  | Value         
----+-------------         ----------+--------+-----------
 1  | Peter                      1   | City   | New York 
 2  | Jane                       1   | Age    | 26
                                 2   | City   | New York
                                 2   | Age    | 50
  1. 我可以在 sql 查询中应用搜索条件,比如age > 25 and city = 'New York'没有groupingpivoting表的人。
  2. 以最少的性能开销应用搜索条件的最佳方法是什么。
4

3 回答 3

2
SELECT key1.PersonId 
FROM KeyValue key1
    INNER JOIN KeyValue key2 ON key1.PersonId = key2.PersonId
WHERE key1.[Field] = 'Age' and key1.[Value] > 25
    AND key2.[Field] = 'City' and key2.[Value] = 'New York' 

更新

我做了一些测试,INNER JOIN 看起来足够快。这里结果和测试脚本

SET NOCOUNT ON 
SET STATISTICS IO ON

CREATE TABLE KeyValue (
    ID INT NOT NULL IDENTITY CONSTRAINT [PK_KeyValue] PRIMARY KEY CLUSTERED
    ,PersonId INT NOT NULL
    ,Field varchar(30) NOT NULL
    ,Value varchar(255) NOT NULL
    ,CONSTRAINT UQ__KeyValue__PersonId_Field UNIQUE (PersonId, Field)
)
GO
--INSERT INTO KeyValue 500K "users", 4 "Fields" - 2M rows

CREATE NONCLUSTERED INDEX [IX__KeyValue__Field_Value_ID]
ON [dbo].[KeyValue] ([Field],[Value]) INCLUDE ([PersonId])
GO

select PersonId from (
    select PersonId, ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY PersonId) RowNumber from (
        select PersonId from KeyValue where [Field] = 'Age' and [Value] > 25 union all
        select PersonId from KeyValue where [Field] = 'City' and [Value] = 'Sydney' union all
        select PersonId from KeyValue where [Field] = 'Email' and [Value] = 'xxxxx@gmail.com' union all
        select PersonId from KeyValue where [Field] = 'Name' and [Value] = 'UserName' 
    ) x
) y where RowNumber = 4
--Table 'KeyValue'. Scan count 20, logical reads 1510, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

select PersonId from (
        select PersonId from KeyValue where [Field] = 'Age' and [Value] > 25 union all
        select PersonId from KeyValue where [Field] = 'City' and [Value] = 'Sydney' union all
        select PersonId from KeyValue where [Field] = 'Email' and [Value] = 'xxxxx@gmail.com' union all
        select PersonId from KeyValue where [Field] = 'Name' and [Value] = 'UserName' 
) x GROUP by PersonId
HAVING COUNT(*) = 4
--Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Table 'KeyValue'. Scan count 4, logical reads 1377, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SELECT key1.PersonId 
FROM KeyValue key1
    INNER JOIN KeyValue key2 ON key1.PersonId = key2.PersonId
    INNER JOIN KeyValue key3 ON key1.PersonId = key3.PersonId
    INNER JOIN KeyValue key4 ON key1.PersonId = key4.PersonId
WHERE key1.[Field] = 'Age' and key1.[Value] > 25
    AND key2.[Field] = 'City' and key2.[Value] = 'Sydney'
    AND key3.[Field] = 'Email' and key3.[Value] = 'xxxxx@gmail.com' 
    AND key4.[Field] = 'Name' and key4.[Value] = 'UserName' 
-- Table 'KeyValue'. Scan count 1, logical reads 21, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SET STATISTICS IO OFF
GO
于 2013-11-07T09:45:17.737 回答
1

您可以使用以下结构:

select PersonId from
(
    select PersonId from xxx where [Field] = 'Age' and [Value] > 25
    union all
    select PersonId from xxx where [Field] = 'City' and [Value] = 'New York' 
) x
group by PersonId
having count(*) = 2

您可以为每个参数的并集再创建一个查询。因此,通过这种方式,它将返回每个PersonId匹配条件的次数。然后您可以选择PersonId符合所有条件的那些,即count(*)等于参数数量。

您可以轻松地将其扩展到更多参数。

如果您在 和 上有正确的索引,这应该会很好地Field执行Value


这是一个没有的版本group by(尽管它具有相同的效果):

select PersonId from
(
    select PersonId, ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY PersonId) RowNumber from
    (
        select PersonId from xxx where [Field] = 'Age' and [Value] > 25
        union all
        select PersonId from xxx where [Field] = 'City' and [Value] = 'Sydney' 
    ) x
) y
where RowNumber = 2
于 2013-11-07T09:38:22.220 回答
0

如果我理解你的问题正确,这样的事情会起作用:

SELECT 
  one.* 
FROM table1 AS one
INNER JOIN table2 AS two1 ON one.Id = two1.PersonId
INNER JOIN table2 AS two2 ON one.Id = two2.PersonId
WHERE (two1.field = 'age' AND two1.value > 25) 
AND (two2.field = 'city' AND two2.value = 'New York')

祝你好运!

于 2013-11-07T09:42:56.820 回答