1

我知道必须有更好的方法来做到这一点,我今天脑死了。

我有两张桌子:

Reference
Id         Label
1          Apple
2          Banana
3          Cherry  

Elements
Id    ReferenceId    P1   P2   Qty
1     1              1    2    8
2     2              2    3    14
3     1              3    2    1
4     3              2    1    6
5     3              1    2    3

我想主要按 (P1, P2) 将它们分组,但独立于 P1 和 P2 的顺序 - 以便 (1,2) 和 (2,1) 映射到同一组。没关系。

另一部分是我想获得给定 P1、P2 对的总和(数量)较大的标签 - 换句话说,我希望结果集为:

 P1   P2  TotalQty  MostRepresentativeLabel
 1    2   17        Cherry
 2    3   15        Banana

我能想到的就是这个可怕的混乱:

select endpoint1, endpoint2, totalTotal, mostRepresentativeLabelByQty from 
(
select SUM(qty)as total
,case when (p1<p2) then p1 else p2 end as endpoint1
,case when (p1<p2) then p2 else p1 end as endpoint2
,reference.label as mostRepresentativeLabelByQty

from elements inner join reference on elements.fkId = reference.id
group by case when (p1<p2) then p1 else p2 end
,case when (p1<p2) then p2 else p1 end
,label
) a inner join 
(
select MAX(total) as highestTotal, SUM(total) as totalTotal from 
(
select SUM(qty)as total
,case when (p1<p2) then p1 else p2 end as endpoint1
,case when (p1<p2) then p2 else p1 end as endpoint2
,reference.label as mostRepresentativeLabelByQty

from elements inner join reference on elements.fkId = reference.id
group by case when (p1<p2) then p1 else p2 end
,case when (p1<p2) then p2 else p1 end
,label
) byLabel
group by endpoint1, endpoint2
) b
on a.total = b.highestTotal

哪个..有效...但我不相信。这最终将在更大的数据集(200,000 行左右)上运行,所以我不喜欢这种方法 - 有没有更简单的方法来表达“使用该列中的值,而其他列被最大化”我'我完全空白?

(SQL Server 2008 R2 by the way)

4

1 回答 1

1

I use the sum of the BINARY_CHECKSUM's of P1 and P2 to uniquely identify each group. This SUM is identified by the BC alias, and permits the grouping needed to find the largest group labels.

DECLARE @Reference TABLE(ID INT, Label VARCHAR(10));
DECLARE @Elements TABLE(ID INT, ReferenceID INT, P1 INT, P2 INT, Qty INT);

INSERT INTO @Reference VALUES
(1,'Apple')
, (2,'Banana')
, (3,'Cherry');

INSERT INTO @Elements VALUES
(1,1,1,2,8)
, (2,2,2,3,14)
, (3,1,3,2,1)
, (4,3,2,1,6)
, (5,3,1,2,3);

; WITH a AS (
    SELECT
    P1, P2=P2, Qty, BC=ABS(BINARY_CHECKSUM(CAST(P1 AS VARCHAR(10))))+ABS(BINARY_CHECKSUM(CAST(P2 AS VARCHAR(10))))
    , Label
    , LabelSum=SUM(Qty)OVER(PARTITION BY ABS(BINARY_CHECKSUM(CAST(P1 AS VARCHAR(10))))+ABS(BINARY_CHECKSUM(CAST(P2 AS VARCHAR(10)))),Label)
    , GroupSum=SUM(Qty)OVER(PARTITION BY ABS(BINARY_CHECKSUM(CAST(P1 AS VARCHAR(10))))+ABS(BINARY_CHECKSUM(CAST(P2 AS VARCHAR(10)))))
    FROM @Elements e
    INNER JOIN @Reference r on r.ID=e.ReferenceID
)
, r AS (
    SELECT *, rnk=RANK()OVER(PARTITION BY BC ORDER BY LabelSum DESC)
    FROM a
)
SELECT P1=MIN(P1)
, P2=MAX(P2)
, TotalQty=GroupSum
, MostRepresentativeLabel=Label
FROM r
WHERE rnk=1
GROUP BY GroupSum,Label
ORDER BY GroupSum DESC;
GO

Result:

enter image description here

EDIT Wrap each BINARY_CHECKSUM in ABS to maximize the entropy of the sums of each group's BINARY_CHECKSUM. Because BINARY_CHECKSUM is a signed BIGINT, this will decrease the chances of a collision between two different groups where a positive BINARY_CHECKSUM is summed with a negative BINARY_CHECKSUM.

于 2012-04-28T22:48:06.193 回答