4

我在 SQLServer 2008 中遇到了一个我不理解的奇怪行为。我想快速将独特的客户与独特的付款配对。

使用这个查询,我得到了我期望的结果。每个 CustomerId 都与不同的 PaymentId 配对。

SELECT CustomerId, PaymentId, RowNumber1, RowNumber2
FROM (
    SELECT
        c.Id as CustomerId,
        p.Id as PaymentId,
        ROW_NUMBER() OVER (PARTITION BY p.Id ORDER BY p.Id) AS RowNumber1,
        ROW_NUMBER() OVER (PARTITION BY c.Id ORDER BY c.Id) AS RowNumber2
    FROM Customer as c
    CROSS JOIN Payment as p
) AS INNERSELECT WHERE RowNumber2 = 1

+------------+-----------+------------+------------+
| CustomerId | PaymentId | RowNumber1 | RowNumber2 |
+------------+-----------+------------+------------+
|          4 |         1 |          1 |          1 |
|          5 |         2 |          2 |          1 |
+------------+-----------+------------+------------+

但是,如果我从外部选择中删除 RowNumber1 列,结果似乎会发生变化。现在 PaymentId 的每个值都是 1,即使我没有触及内部 select 语句。

SELECT CustomerId, PaymentId, RowNumber2
FROM (
    SELECT
        c.Id as CustomerId,
        p.Id as PaymentId,
        ROW_NUMBER() OVER (PARTITION BY p.Id ORDER BY p.Id) AS RowNumber1,
        ROW_NUMBER() OVER (PARTITION BY c.Id ORDER BY c.Id) AS RowNumber2
    FROM Customer as c
    CROSS JOIN Payment as p
) AS INNERSELECT WHERE RowNumber2 = 1

+------------+-----------+------------+
| CustomerId | PaymentId | RowNumber2 |
+------------+-----------+------------+
|          4 |         1 |          1 |
|          5 |         1 |          1 |
+------------+-----------+------------+

谁能向我解释为什么从外部选择中删除一列会导致 PaymentId 列中的值发生变化?我可以使用什么其他方法来实现我想要的目标而不需要结果集中的行号?

4

1 回答 1

5

这是因为您的子查询中的 row_number() 的顺序通常没有定义。进行交叉连接时,行可以按任何顺序排列

它可能是:

CUSTOMERID PAYMENTID
         4         1    
         4         2    
         5         2    
         5         1

或者它可能是

CUSTOMERID PAYMENTID
         4         1    
         4         2    
         5         1    
         5         2

当您通过 CUSTOMERID 计算第一个记录集分区中的 row_number 时,您会得到

CUSTOMERID PAYMENTID ROWNUMBER
         4         1         1  
         4         2         2
         5         2         1
         5         1         2

当您通过 CUSTOMERID 计算第二个记录集分区中的 row_number 时,您会得到

CUSTOMERID PAYMENTID ROWNUMBER
         4         1         1  
         4         2         2
         5         1         1
         5         2         2

如果您只想将随机客户和付款配对,您可以这样做

with cte_cust as (
    select id, row_number() over (order by id) as row_num
    from Customer
), cte_pay as (
    select id, row_number() over (order by id) as row_num
    from Payment
)
select
    c.id as CustomerId,
    p.id as PaymentId
from cte_cust as c
    inner join cte_pay as p on p.row_num = c.row_num

请注意,如果您的客户多于付款,则某些客户将不会出现在结果中(反之亦然)。

sql fiddle demo

于 2013-08-21T19:25:55.000 回答