复杂的SQL查询类似于z顺序问题

时间:2010-04-01 00:17:36

标签: sql sql-server

我在MS SQL Server中遇到了一个复杂的SQL问题,在一张纸上绘图时,我意识到我可以把它想象成一个填充了矩形的单个条形图,每个矩形都有不同Z顺序的分段。实际上它根本与z顺序或图形无关,而更多地与一些难以解释的复杂业务规则有关。无论如何,如果有人有关于如何解决下面的想法,那将给我解决方案。

我有以下数据:

ObjectID  |  PercentOfBar  |  ZOrder (where smaller is closer)
---------------------------------------------------------------
A         |  100           |  6
B         |  50            |  5
B         |  50            |  4
C         |  30            |  3
C         |  70            |  6

我想要的查询结果是这个,按任何顺序:

PercentOfBar  |  ZOrder
-------------------------
50            |  5
20            |  4
30            |  3

想象一下,如果我绘制了矩形A,它将填充100%的条形并且z顺序为6。

6666666666
AAAAAAAAAA

如果我然后布置由两个段组成的矩形B,则两个段都将覆盖矩形A,从而导致以下渲染:

4444455555
BBBBBBBBBB

根据经验,对于给定的矩形,它的分段应该布置成使得最高的z顺序位于较低Z阶的右侧。

最后矩形C将仅覆盖矩形B的部分,其中30%的片段是z阶3,它将在左侧。您可以希望看到在上面列出的输出数据集中如何表示:

3334455555
CCCBBBBBBB

现在为了使事情变得更复杂,我实际上有第4列,这样每个键都会发生这种分组:

输入:

SomeKey, ObjectID, PercentOfBar, ZOrder (where smaller is closer)
X, A, 100, 6
X, B, 50, 5
X, B, 50, 4
X, C, 30, 3
X, C, 70, 6
Y, A, 100, 6
Z, B, 50, 2
Z, B, 50, 6
Z, C, 100, 5

输出:

SomeKey, PercentOfBar, ZOrder
X, 50, 5
X, 20, 4
X, 30, 3
Y, 100, 6
Z, 50, 2
Z, 50, 5

请注意,在输出中,每个SomeKey的PercentOfBar将加起来为100%。

我知道这是我今晚睡觉时会想到的。

只是要明确并提出问题:

会产生上述结果的查询是什么?

1 个答案:

答案 0 :(得分:1)

我做了以下假设:

  • 您正在使用SQL Server 2005或更新版本。
  • SomeKey,ObjectID,ZOrder是唯一的。

其他说明:

  • 我没有优化查询 - 我只是试图获得正确的结果。
  • 我只测试了你的测试数据。

考虑到这一点你可以尝试这样的事情:

WITH Bars AS (
    SELECT
        T1.SomeKey,
        T1.ObjectID,
        T1.ZOrder,
        SUM(T2.PercentOfBar) - T1.PercentOfBar AS PercentStart,
        SUM(T2.PercentOfBar) AS PercentEnd
    FROM Table1 T1
    JOIN Table1 T2
    ON T1.SomeKey = T2.SomeKey
        AND T1.ObjectID = T2.ObjectID
        AND T1.ZOrder >= T2.ZOrder
    GROUP BY T1.SomeKey, T1.ObjectID, T1.PercentOfBar, T1.ZOrder),
Boundaries AS (
    SELECT P, ROW_NUMBER() OVER (ORDER BY P) AS rn
    FROM (
        SELECT DISTINCT PercentStart AS P FROM Bars
        UNION
        SELECT DISTINCT PercentEnd FROM Bars
    ) T1),
Intervals AS (
    SELECT B1.P AS PercentStart, B2.P AS PercentEnd
    FROM Boundaries B1
    JOIN Boundaries B2
        ON B1.rn + 1 = B2.rn),
Bits AS (
    SELECT
        SomeKey,
        ObjectId,
        ZOrder,
        Intervals.PercentStart,
        Intervals.PercentEnd
    FROM Intervals
    JOIN Bars
        ON Bars.PercentStart <= Intervals.PercentStart
        AND Bars.PercentEnd >= Intervals.PercentEnd),
LowestZOrder AS (
    SELECT SomeKey, PercentStart, MIN(ZOrder) AS ZOrder
    FROM Bits
    GROUP BY SomeKey, PercentStart),
LowestBits AS (
    SELECT Bits.*
    FROM Bits
    JOIN LowestZOrder
        ON Bits.SomeKey = LowestZOrder.SomeKey
        AND Bits.PercentStart = LowestZOrder.PercentStart
        AND Bits.ZOrder = LowestZOrder.ZOrder)
SELECT
    SomeKey,
    MAX(PercentEnd) - MIN(PercentStart) AS PercentOfBar,
    ZOrder
FROM LowestBits
GROUP BY SomeKey, ObjectID, ZOrder
ORDER BY SomeKey, ObjectID, MIN(PercentStart) DESC

结果:

SomeKey PercentOfBar ZOrder
X       50           5
X       20           4
X       30           3
Y       100          6
Z       50           2
Z       50           5

测试数据:

CREATE TABLE Table1 (SomeKey NVARCHAR(100) NOT NULL, ObjectID NVARCHAR(100) NOT NULL, PercentOfBar INT NOT NULL, ZOrder INT NOT NULL);
INSERT INTO Table1 (SomeKey, ObjectID, PercentOfBar, ZOrder) VALUES
('X', 'A', 100, 6),
('X', 'B', 50, 5),
('X', 'B', 50, 4),
('X', 'C', 30, 3),
('X', 'C', 70, 6),
('Y', 'A', 100, 6),
('Z', 'B', 50, 2),
('Z', 'B', 50, 6),
('Z', 'C', 100, 5);