【问题标题】:SQL query one to many relationship join without duplicatesSQL查询一对多关系连接,不重复
【发布时间】:2013-07-23 00:36:33
【问题描述】:

我在 SQL Server 2008 中运行查询。我有一个 sales 表和一个 payments 表。有时销售有多种付款方式(部分礼品卡 + 部分现金或部分信用卡 + 部分现金等..) 所以我想做的是在表格中列出每个销售的销售和付款。

如果我执行LEFT JOIN ON sales.SaleID = payments.SaleID,当有多个匹配的付款行时,我会得到重复的销售行..

所以我一直在做的是获取所有销售额并计算有多少与(SELECT COUNT(*) FROM payments WHERE payments.SaleID = sales.SaleID) AS NumOfPayments 匹配的付款行。然后在我的 PHP 脚本中检查付款次数,如果是 > 1,然后运行另一个查询以获取付款详细信息。

我想要得到的输出看起来像这样

-----------------------------------------------------
| SaleID |  SaleDate  | Amount  | Payments          |
-----------------------------------------------------
|    123 | 2013-07-23 | $ 19.99 | Cash:     $ 19.99 |
|    124 | 2013-07-23 | $  7.53 | Cash:     $  7.53 |
|    125 | 2013-07-23 | $174.30 | Credit:   $124.30 |
|        |            |         | GiftCard: $ 50.00 |
|    126 | 2013-07-23 | $ 79.99 | Cash:     $ 79.99 |
|    127 | 2013-07-23 | $100.00 | Credit:   $ 90.00 |
|        |            |         | Cash:     $ 10.00 |
-----------------------------------------------------

销售 125 和 127 列出了多笔付款,但销售信息只出现一次,并且每次付款都不会重复。

salespayments 表如下所示:

Sales                              Payments
---------------------------------  --------------------------------------------
| SaleID |  SaleDate  | Amount  |  | PaymentID | SaleID | PmtMethod |  PmtAmt |
---------------------------------  --------------------------------------------
|    123 | 2013-07-23 | $ 19.99 |  |       158 |    123 |        4  | $ 19.99 |
|    124 | 2013-07-23 | $  7.53 |  |       159 |    124 |        4  | $  7.53 |
|    125 | 2013-07-23 | $174.30 |  |       160 |    125 |        2  | $124.30 |
|    126 | 2013-07-23 | $ 79.99 |  |       161 |    125 |        3  | $ 50.00 |
|    127 | 2013-07-23 | $100.00 |  |       162 |    126 |        4  | $ 79.99 |
---------------------------------  |       163 |    127 |        2  | $ 90.00 |
                                   |       164 |    127 |        4  | $ 10.00 |
                                   --------------------------------------------

我觉得如果我只用 SQL 就能做到这一点,它会更快。有没有办法用纯 SQL 来实现这一点,而不必使用服务器端代码来运行条件查询。

【问题讨论】:

  • 所有销售在支付表中至少有一条记录,对吗?
  • @VasanthSundaralingam 是的,每笔销售都应至少有一个相应的付款记录

标签: sql sql-server sql-server-2008 tsql join


【解决方案1】:

我不会将数据检索和数据显示混为一谈,我认为这就是您要问的问题。您是否有某种列来指示应首先显示哪个付款?我在想这样的事情:

SELECT columnlist, 
rn = ROW_NUMBER() OVER (PARTITION BY sales.salesID ORDER BY payment.paymentID)
FROM sales JOIN payments ON sales.salesID=payments.salesID

然后,在您的 GUI 中,仅显示 RN = 1 的前 3 列的值,并将 RN > 1 的值清空。

【讨论】:

  • 我已经使用 mysql 5.6 尝试过这种方法,它给了我惊人的具体消息“你的 SQL 语法有错误”:(
  • MySQL 不是 SQL Server :) 我认为 MySQL 不支持 ROW_NUMBER() 功能。
【解决方案2】:

在界面中这样做可能更容易。

你想要的基本查询是:

  select s.saleID, s.SaleDate, s.Amount,
         p.PaymentType, p.PaymentAmount,
         ROW_NUMBER() over (partition by p.SaleId order by p.PaymentAmount desc) as seqnum
  from sales s join
       payments p
       on p.saleID = s.saleId
  order by 1, 2

但是,您正在尝试空白字段。为此,您需要将所有字段转换为字符串,然后检查每个 SaleId 的第一行是否包含哪些字段:

select (case when seqnum > 1 then '' else CAST(SaleId as varchar(255)) end) as SaleId,
       (case when seqnum > 1 then '' else CONVERT(varchar(10), SaleDate, 121) end) as SaleDate,
       (case when seqnum > 1 then '' else '$'+STR(amount, 6, 2) end) as Amount,
       PaymentType, PaymentAmount
from (select s.saleID, s.SaleDate, s.Amount,
             p.PaymentType, p.PaymentAmount,
             ROW_NUMBER() over (partition by p.SaleId order by p.PaymentAmount desc) as seqnum
      from sales s join
           payments p
           on p.saleID = s.saleId
     ) sp
order by SaleId, SaleDate;

这不是 SQL 设计的操作类型。 SQL 与表一起使用,其中所有列都具有相同的含义。在这里,您将根据其在销售中的位置为该列引入不同的含义。是的,SQL 可以做到。不,这并不容易。

【讨论】:

    【解决方案3】:

    以下查询将给出您需要的结果,您需要跳过 SalesIDToSort 列。

      ;with SalesData
      as
      (
        select 
          Sales.SalesID, Sales.SalesDate, Sales.TotalAmount, Payments.PaymentMode, Payments.Amount,
          Row_Number() over(Partition by Sales.SalesID order by Payments.PaymentID) RowNum
        from 
          Payments
          inner join Sales on Payments.SalesID = Sales.SalesID
      )
      select SalesID, SalesDate, TotalAmount, PaymentMode, Amount, SalesID SalesIDToSort
      from 
        SalesData
      where
        SalesData.RowNum = 1
      union all
      select null, null, null, PaymentMode, Amount, SalesID SalesIDToSort
      from 
        SalesData
      where
        SalesData.RowNum > 1
      order by 6
    

    Working demo

    【讨论】:

      【解决方案4】:

      您可以尝试按功能分类,例如 Format to:

      select salesid, salesDate, Amount, FORMAT(payment, 9999.999) as payments
      from FROM payments 
      WHERE payments.SaleID = sales.SaleID
      group by salesid;
      

      如果您将 FORMAT 作为可用函数,这可能会起作用。

      【讨论】:

        猜你喜欢
        • 2019-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-21
        • 1970-01-01
        相关资源
        最近更新 更多