如何从表中选择而不包含重复的列值?

Wil*_*Cau 5 sql-server-2008

在上一个问题中如何合并数据集而不包括冗余行?我询问了在导入过程中过滤冗余历史数据的问题,但@DavidSpillett 正确回答说我无法做我想做的事情。

我现在不想在导入过程中过滤表,而是想在表上创建一个视图,该视图仅返回价格发生变化的记录。

这是为解决这个问题而改写的原始场景:

我们有一个物品的历史价格表。该表包含多个日期记录相同价格的行。我想在此数据上创建一个视图,该视图仅显示价格随时间的变化,因此如果价格从 A 更改为 BI 想要查看它,但如果它从 B“更改”为 B 那么我不想看到它.

例子:如果昨天的价格是$1,今天的价格是$1,并且没有其他价格变化,那么今天的价格可以从昨天的价格推断出来,所以我只需要昨天的记录。

示例(http://sqlfiddle.com/#!3/c95ff/1):

Table data:

Effective            Product  Kind  Price
2013-04-23T00:23:00  1234     1     1.00
2013-04-24T00:24:00  1234     1     1.00 -- redundant, implied by record 1
2013-04-25T00:25:00  1234     1     1.50
2013-04-26T00:26:00  1234     1     2.00
2013-04-27T00:27:00  1234     1     2.00 -- redundant, implied by record 4
2013-04-28T00:28:00  1234     1     1.00 -- not redundant, price changed back to 1.00

Expected view data:

Effective            Product  Kind  Price
2013-04-23T00:23:00  1234     1     1.00
2013-04-25T00:25:00  1234     1     1.50
2013-04-26T00:26:00  1234     1     2.00
2013-04-28T00:28:00  1234     1     1.00
Run Code Online (Sandbox Code Playgroud)

我最初的尝试使用 ROW_NUMBER:

SELECT
    Effective,
    Product,
    Kind,
    Price
FROM
(
    SELECT
        History.*,
        ROW_NUMBER() OVER
        (
            PARTITION BY
                Product,
                Kind,
                Price
            ORDER BY
                Effective ASC
        ) AS RowNumber
    FROM History
) H
WHERE RowNumber = 1
ORDER BY Effective
Run Code Online (Sandbox Code Playgroud)

哪个返回:

Effective               Product  Kind  Price
2013-04-23T00:23:00     1234     1     1.00
                                             -- not 2013-04-24, good
2013-04-25T00:25:00     1234     1     1.50
2013-04-26T00:26:00     1234     1     2.00
                                             -- not 2013-04-27, good
                                             -- not 2013-04-28, bad
Run Code Online (Sandbox Code Playgroud)

我尝试搜索类似的问题/答案,但很难弄清楚如何表达搜索,一个例子值得很多词。

任何建议表示赞赏。谢谢

Pau*_*ite 7

SQLfiddle

SELECT H.*
FROM History AS H
OUTER APPLY
(
    SELECT TOP (1)
        H2.Price
    FROM History AS H2
    WHERE
        H2.Product = H.Product
        AND H2.Kind = H.Kind
        AND H2.Effective < H.Effective
    ORDER BY
        H2.Effective DESC
) AS X
WHERE
    NOT EXISTS (SELECT X.Price INTERSECT SELECT H.Price);
Run Code Online (Sandbox Code Playgroud)

执行计划

对于问题中给出的少量行,这是一个很好的执行计划。对于较大的表,此查询的理想索引是:

CREATE UNIQUE INDEX [dbo.History Product, Kind, Effective]
ON dbo.History (Product, Kind, Effective DESC)
INCLUDE (Price);
Run Code Online (Sandbox Code Playgroud)

执行计划2

该索引本质上是按更有用的顺序排列的聚集索引键。根据表用于其他查询的方式,最好替换聚集索引而不是创建此新索引。

不要使用批量估计成本百分比来比较不同的查询。一般来说,这不是一个有效的比较。成本始终是优化器估计,而不是打算以这种方式使用。一定要检查实际的性能指标(运行时间、I/O、CPU 使用率、内存使用率),但不要相信百分比。

这种查询还是基于查询的ROW_NUMBER效率更高取决于数据的分布和其他因素。每个都有其优点和缺点。当每个(产品、种类)组合有很多行时,此查询具有优势。


AK_*_*AK_ 5

SELECT  *
FROM   (SELECT TOP 1 *
        FROM    History
        ORDER BY Effective ASC) AS f

UNION ALL

SELECT  *
FROM    History AS a
WHERE   a.Price <> (SELECT TOP 1 Price
                    FROM    History AS b
                    WHERE   b.Effective < a.Effective 
                    ORDER BY Effective DESC) 

ORDER BY Effective ASC ;
Run Code Online (Sandbox Code Playgroud)

SQL-Fiddle测试

执行计划

  • 我认为它需要一个 `UNION ALL` 来包含表中的第一行(由于与 NULL 的比较,它不会被返回),但它应该像 OP 想要的那样工作。 (2认同)