【问题标题】:SQL Join Tables on Closest Date BEFORE Shipped DateSQL 在发货日期之前的最近日期连接表
【发布时间】:2017-02-28 03:23:28
【问题描述】:

这似乎是一个重复的问题,但这并不是因为论坛中的其他解决方案在这种情况下不起作用。

这是对我们的 ERP 数据库的查询,它试图获取零件的最终销售总成本。基本上,ERP 可以轻松计算出所有直接成本,但不计算报废成本。

我被卡住的地方是在 FROM 部分的子查询中标记:

>>>>>>HELP NEEDED STARTING HERE

现在编写的子查询将所有货物提取给我们的废品供应商并获得每磅的月平均费率,然后根据合金类型、月份和年份加入其他表。

我的财务部门告诉我,平均价格不是一个好的解决方案,因为一些金属价格波动太大,或者他们没有在零件发货的同一个月出售废金属,所以这行不通。

我需要从零件从我们工厂发货之前的最近日期获得我们为废金属支付的费率。

我在 Stack Overflow 上找到了其他示例,这些示例显示了执行此操作的方法,但主表和子查询表重叠,因此我看到的其他解决方案都失败了。我在下面的代码中进行了注释以显示和解释这一点。

我完全可以接受我处理这个错误的想法。我怎样才能做到这一点?

DECLARE @Date_From                      AS  DATETIME;
DECLARE @Date_To                        AS  DATETIME;
SET     @Date_From                      = '2016-10-01 00:00:00.000';
SET     @Date_To                        =  GETDATE()       ;
 -- Start Main query     
SELECT TOP 10
   CCustomer.Customer_Type          AS  'Industry'
  ,SShipper.Ship_Date               AS  'Ship_Date'
  ,SSContainer.Serial_No            AS  'Serial_No'
  ,PPart.Grade                      AS  'Alloy'
  ,tbl_ScrapValue.Scrap_Value_per_lb    AS 'Scap_Value_per_lb'

FROM
            Sales_v_Shipper_Line          AS SSLine 
      JOIN  Sales_v_Shipper               AS SShipper
        ON  SShipper.Shipper_Key = SSLine.Shipper_Key
      JOIN  Part_v_Part                   AS PPart
        ON  SSLine.Part_Key = PPart.Part_Key
      JOIN  Common_v_Customer             AS CCustomer
        ON  SShipper.Customer_No   = CCustomer.Customer_No
 -- >>>>>>HELP NEEDED STARTING HERE
 -- Below is the sub query that pulls the scrap sales value per pound.
 --  The key point is that both shipments to our customers of real parts,
 --   and the 'shipments' of scrap metal sales come from the same tables,
 --  mainly Part_v_Part and Sales_v_Shipper, because of that the other 
--solutions for the 'join by closest date' in the forums don't work.

LEFT OUTER  JOIN  (SELECT
                     MONTH(SShipper.Ship_Date)       AS 'Scrap_Ship_Month'
                    ,YEAR(SShipper.SHip_Date)        AS 'Scrap_Ship_Year'
                    ,PPart.Grade                     AS  'Alloy'
                    ,AVG(AARIDist.Unit_Price)        AS 'Scrap_Value_per_lb'

                FROM
                        Sales_v_Shipper               AS  SShipper
                  JOIN  Sales_v_Shipper_Line          AS  SS_Line
                    ON  SShipper.Shipper_Key = SS_Line.Shipper_Key  
                  JOIN  Part_v_Part                   AS PPart
                    ON  SS_Line.Part_Key = PPart.Part_Key
                  JOIN  Common_v_Customer             AS  CCustomer
                    ON  SShipper.Customer_No = CCustomer.Customer_No

                WHERE CCustomer.Customer_Code     = 'Scrap_Vendor'
                AND SSHipper.Ship_Date <= @Date_To

            GROUP BY
                 MONTH(SShipper.Ship_Date)
                ,YEAR(SShipper.SHip_Date)
                ,PPart.Grade
               ) AS tbl_ScrapValue
  ON  PPart.Grade = tbl_ScrapValue.Alloy 
      AND
        YEAR(SShipper.Ship_Date) = YEAR(tbl_ScrapValue.Scrap_Ship_Year)
      AND
        MONTH(SShipper.Ship_Date) =(tbl_ScrapValue.Scrap_Ship_Month)

--- >>>>HELP NEEDED ENDS HERE
WHERE
    AND SShipper.Ship_Date >= @Date_From
    AND SSHipper.Ship_Date <= @Date_To      
 GROUP BY
      SShipper.Shipper_No
      ,SShipper.Ship_Date
      ,CCustomer.Customer_Type
      ,SSContainer.Quantity
      ,PPart.Grade

这是上面查询的示例输出,您可以看到“Scrap_Value_per_lb”失败:

[![Sample_Output][1]][1]

Industry              Ship_Date         Serial_No Alloy Scap_Value_per_lb
Material Processing 17-Oct-16 4:47:00 PM S472091  C182 NULL 
Material Processing 17-Oct-16 4:47:00 PM S472210  C182 NULL 
Material Processing 17-Oct-16 4:47:00 PM S472211  C182 NULL 
Electronics         17-Oct-16 4:27:00 PM S436738  C180 NULL 
Electronics         17-Oct-16 4:27:00 PM S463290  C180 NULL 
Electronics         17-Oct-16 4:27:00 PM S463315  C180 NULL 
Electronics         17-Oct-16 4:27:00 PM S463327  C180 NULL 
Electronics         17-Oct-16 4:27:00 PM S463333  C180 NULL 
Electronics         17-Oct-16 4:27:00 PM S463345  C180 NULL 
Electronics         17-Oct-16 4:27:00 PM S463354  C180 NULL 

更新

这是在 2016 年 10 月 19 日上午 7 点进行了第二次编辑,以进一步简化代码,在代码中添加了 cmets 以根据其他人的反馈进行澄清。

【问题讨论】:

  • 这是您能想出的最短的问题示例?不要只是在这里转储你的代码,做一个简短的例子。
  • @JamesZ,这是一个简短的例子,我花了大约 30 分钟剪掉了很多。我想总会有更多的削减,但这大约是我开始时的 25%。我会根据您的反馈进行一些修改,但我想强调这不是我的代码的直接转储。
  • 您使用的是什么版本的 SQL Server?
  • 不幸的是,至少我无法弄清楚这里的问题是什么,有太多的表和连接,很难弄清楚。也许你需要使用外部应用而不是加入,选择顶部 1 + 有按日期顺序排列的顺序吗?
  • @FeryalBadili,T-SQL,感谢您的参与!

标签: sql-server tsql left-join inner-join closest


【解决方案1】:

我花了几个小时思考这个问题,并将示例代码进一步简化为re-posted the question here

我认为这更有意义,我感谢所有回复并试图帮助回答我所写内容的人。它有所帮助,但我仍然无法让它工作。

【讨论】:

    【解决方案2】:

    我不知道我是否解决了您的问题。您想通过在日期字段上使用 JOIN 来连接两个结果集,其中不能保证完全匹配。可能您可以使用 ROW_NUMBER 函数生成包括排序在内的分区行号,然后使用例如加入行号。 ROW_NR = 1。

    TABLE 1                 TABLE 2
    ROW_NR DATE       ID    ROW_NR DATE       ID
    ------ ---------- --    ------ ---------- --
         1 10/25/2016  1 -match- 1 10/27/2016  1 
         2 10/24/2016  1        
         3 10/20/2016  1
         4 10/19/2016  1
         1 10/23/2016  2 -match- 1 10/28/2016  2
         2 10/15/2016  2        
         3 10/09/2016  2
         4 10/08/2016  2     
    

    表 1 中的行号:

    • TABLE1.DATE

    • 按 ID 分区

    • 按 ID 和 DATE DESC 排序

    表 2 中的行号:

    • ROW_NR 始终为 1

    有了它,您可以在没有完全匹配的情况下隐式连接数据字段。很抱歉没有提供 SQL 语句。

    【讨论】:

      【解决方案3】:

      因此,当您出售某种合金的金属时,您会出售所有可用的东西,即没有剩余物?

      然后,您从购买表中取出记录,并将它们与同一合金的下一个销售记录连接起来。这可以通过交叉应用来实现。这是一个带有简单表格的查询,可让您了解需要什么:

      select
        year(matched_sold.sold_date),
        month(matched_sold.sold_date),
        sum(bought.amount * bought.price)
      from bought
      cross apply
      (
        select top 1 *
        from sold
        where sold.alloy = bought.alloy
        and sold.sold_date > bought.bought_date
        order by sold.sold_date desc
      ) matched_sold
      group by
        year(matched_sold.sold_date),
        month(matched_sold.sold_date);
      

      【讨论】:

      • 我们将金属零件出售给“真正的”客户,并将制造过程中取出的废金属出售给废品场。我们将我们的废料供应商列为客户并将废料像其他产品一样运送给他,因此使用的内部查询表和外部查询表是相同的。实际上,我正在尝试根据日期最接近的匹配来匹配来自同一张表的 2 批货物。外部查询是发给真实客户的货物,内部查询将货物运送到废品场,我从中得出每磅的价格以获取 COGS。我正在尝试根据合金和最近的日期加入这些。
      【解决方案4】:

      所以,如果我理解正确,您希望子查询只返回一行(外部查询中的每一行都相同?)

      SELECT Top 1 
                           SShipper.Ship_Date
                          ,PPart.Grade                AS  'Alloy'
                          ,AARIDist.Unit_Price        AS 'Scrap_Value_per_lb'
      
                      FROM
                              Sales_v_Shipper               AS  SShipper
                        JOIN  Sales_v_Shipper_Line          AS  SS_Line
                          ON  SShipper.Shipper_Key = SS_Line.Shipper_Key  
                        JOIN  Part_v_Part                   AS PPart
                          ON  SS_Line.Part_Key = PPart.Part_Key
                        JOIN  Common_v_Customer             AS  CCustomer
                          ON  SShipper.Customer_No = CCustomer.Customer_No
                        JOIN  Accounting_v_AR_Invoice_Dist  AS  AARIDist
                          ON  SS_Line.Shipper_Line_Key = AARIDist.Shipper_Line_Key
      
                      WHERE CCustomer.Customer_Code     = 'Scrap_Value'
                      AND SSHipper.Ship_Date <= @Date_To
                      AND AARIDIst.Unit_Price < AARIDist.Quantity
                      AND AARIDist.Unit_Price > '0'
      
                      Order By SShipper.Ship_Date Desc
      
                     ) AS tbl_SValue
      

      如果外查询的每一行子查询的值应该不同,那么我需要知道子查询中的每一行是如何连接到外查询中的行的

      【讨论】:

      • 我想根据 PPart.Grade(别名为“合金”)的匹配将子查询连接到外部查询中的行,并且废品发货日期最接近发货日期的一部分。我认为我遇到的问题是我们将我们的废料供应商列为客户并将废料运送给他,因此内部查询表和外部查询表是相同的。实际上,我正在尝试根据日期最接近的匹配来匹配来自同一张表的 2 批货物。因此,我发现的所有其他潜在解决方案都不起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-23
      • 2014-12-12
      • 2017-06-29
      • 2020-11-13
      • 1970-01-01
      相关资源
      最近更新 更多