一对多的SQL查询具有良好的性能

时间:2012-12-27 10:31:05

标签: sql

有人可以帮帮我吗?我正在尝试编写一个SQL查询,并在2-3天内遇到问题。让我先说明问题。

我有2张桌子

  1. Payment_Schedule_Master
  2.     [PAYMENT_SCHEDULE_MASTER_ID] [int] NOT NULL, Primary key
        [FPI_ID] [varchar](9) NOT NULL,
        [DELETE_FLAG] [char](1) NOT NULL,
        [CREATED_BY] [varchar](30) NOT NULL,
        [CREATED_DATE] [datetime] NOT NULL,
        [MODIFY_BY] [varchar](30) NOT NULL,
        [MODIFY_DATE] [datetime] NOT NULL
    1. Payment_Schedule_Detail
    2.     [PAYMENT_SCHEDULE_DETAIL_ID] [int] IDENTITY(1,1) NOT NULL, Primary key
          [PAYMENT_SCHEDULE_MASTER_ID] [int] NOT NULL, Foreign key to master table
          [PAY_YEAR] [int] NOT NULL,
          [PAY_MONTH] [int] NOT NULL,
          [ACTUAL] [money] NULL,
          [FORECAST] [money] NULL,
          [DELETE_FLAG] [char](1) NOT NULL,
          [CREATED_BY] [varchar](30) NOT NULL,
          [CREATED_DATE] [datetime] NOT NULL,
          [MODIFY_BY] [varchar](30) NOT NULL,
          [MODIFY_DATE] [datetime] NOT NULL

      两者之间存在一对多的关系:Master有一个条目,detail有很多条。 Payment_Schedule_Detail有一个id,外键,实际,预测和许多列。实际和预测将包含数值。

      问题:
      我希望获得Payment_Schedule_MasterActual等于0的ForeCast行。

      我的查询:

      我试过了这个查询

      Select 
         t.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 
         t.ActualSum, t.ForecastSum
      from 
          (Select 
              SUM(Actual) As ActualSum, 
              SUM (forecast) AS ForecastSum, 
              PAYMENT_SCHEDULE_MASTER_ID
           from 
              [dbo].[PAYMENT_SCHEDULE_DETAIL]
           group by 
              PAYMENT_SCHEDULE_MASTER_ID) t
      Inner Join 
          dbo.PAYMENT_SCHEDULE_MASTER psm on psm.PAYMENT_SCHEDULE_MASTER_ID = t.PAYMENT_SCHEDULE_MASTER_ID
      where  
          t.ActualSum = t.ForecastSum
      and t.ActualSum = 0
      

      此查询的问题在于,如果Actual在1月份为200,在12月份为200,则会选择该标题,因为SUM (Actual)将为0,这是错误的。

      我不知道如何修改查询,它应该只获得实际为0且预测为0的标题。

      测试
      如果有人会让我知道如何测试该方法?

      更新:尝试了此查询,但需要8秒钟。

      Select 
          t.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 
          t.ActualSum, t.ForecastSum, psd.ACTUAL, psd.FORECAST
      from 
          (Select 
               SUM(Actual) As ActualSum, SUM (forecast) AS ForecastSum, 
               PAYMENT_SCHEDULE_MASTER_ID
           from 
                [dbo].[PAYMENT_SCHEDULE_DETAIL]
           group by 
                PAYMENT_SCHEDULE_MASTER_ID) t
      Inner Join 
           dbo.PAYMENT_SCHEDULE_MASTER psm on psm.PAYMENT_SCHEDULE_MASTER_ID = t.PAYMENT_SCHEDULE_MASTER_ID
      Inner Join 
           [dbo].[PAYMENT_SCHEDULE_DETAIL] psd on psm.PAYMENT_SCHEDULE_MASTER_ID = psd.PAYMENT_SCHEDULE_MASTER_ID
       where  
           t.ActualSum = t.ForecastSum
           and t.ActualSum = 0
           and psd.ACTUAL = 0 
      order by 
           psm.FPI_ID
      

      数据和输出:

      psm_id  Actual      ForeCast  [other columns]
      900     10000.00    0.00
      900     -10000.00   0.00
      900     0.00        0.00
      912     0.00        0.00
      912     0.00        0.00
      912     0.00        0.00
      

      psm_id = Payment_Schedule_Master_ID

      Payment_Schedule_Master_Id 900实际总和为0,不应出现在结果上。但是912会出现在结果中,因为所有记录都是0.我希望这会有所帮助。

8 个答案:

答案 0 :(得分:3)

怎么样 -

select * from Payment_Schedule_Master
where NOT EXISTS
 (
     SELECT  null FROM  Payment_Schedule_Detail 
        where (forecast != 0 or actual != 0) and 
        Payment_Schedule_Master.id = PAYMENT_SCHEDULE_MASTER_ID
 ) 

注意,这也会找到Payment_Schedule_Master,它没有任何相关的payment_schedule_details行。

答案 1 :(得分:0)

要测试你可以使用像SQL Fiddle这样的东西。

尝试this

我尝试以下SQL

select *
from Payment_Schedule_Master mast
where ((select sum(actual) from Payment_Schedule_Detail where main = mast.id) = 0)
and ((select sum(forecast) from Payment_Schedule_Detail where main = mast.id) = 0)

它更简单,我认为它可以满足您的需求

答案 2 :(得分:0)

Select 
    t.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 
    t.ActualSum, t.ForecastSum, psd.ACTUAL, psd.FORECAST
from 
    (Select t1.ActualSum,t1.ForecastSum,t1.PAYMENT_SCHEDULE_MASTER_ID
    from
    (Select 
         SUM(Actual) As ActualSum, SUM (forecast) AS ForecastSum, PAYMENT_SCHEDULE_MASTER_ID
     from 
          [CILS].[dbo].[PAYMENT_SCHEDULE_DETAIL]
     group by 
          PAYMENT_SCHEDULE_MASTER_ID) t1
          where  
     t1.ActualSum = 0 and t1.ForecastSum=0)t    

Inner Join 
     [Cils].dbo.PAYMENT_SCHEDULE_MASTER psm on psm.PAYMENT_SCHEDULE_MASTER_ID = t.PAYMENT_SCHEDULE_MASTER_ID
Inner Join 
     [CILS].[dbo].[PAYMENT_SCHEDULE_DETAIL] psd on psm.PAYMENT_SCHEDULE_MASTER_ID = psd.PAYMENT_SCHEDULE_MASTER_ID
order by 
     psm.FPI_ID

答案 3 :(得分:0)

您的原始查询非常接近您的需求。您正在寻找实际和预测始终为零的情况。好吧,如果它们总是0,那么绝对值之和为0.所以试试这个:

Select 
   t.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 
   t.ActualAbsSum, t.ForecastAbsSum
from 
    (Select 
        SUM(abs(Actual)) As ActualAbsSum, 
        SUM(abs(forecast)) AS ForecastAbsSum, 
        PAYMENT_SCHEDULE_MASTER_ID
     from 
        [dbo].[PAYMENT_SCHEDULE_DETAIL]
     group by 
        PAYMENT_SCHEDULE_MASTER_ID) t
Inner Join 
    dbo.PAYMENT_SCHEDULE_MASTER psm on psm.PAYMENT_SCHEDULE_MASTER_ID = t.PAYMENT_SCHEDULE_MASTER_ID
where  
    t.ForecastAbsSum = 0 and t.ActualAbsSum = 0

您是否想要获得0的任何示例,如:

Select psm.*
from dbo.PAYMENT_SCHEDULE_MASTER psm 
where psm.PAYMENT_SCHEDULE_MASTER_ID in (select PAYMENT_SCHEDULE_MASTER_ID
                                         from [PAYMENT_SCHEDULE_DETAIL]
                                         where actual = 0 and forecast = 0
                                        )

为了清楚起见,它使用in而不是join(两者都在SQL Server引擎中以相同的方式实现)。

答案 4 :(得分:0)

在我看来,在您总结之前,您需要在“实际”和“预测”列中使用哪个位置:

Select 
   t.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 
   t.ActualSum, t.ForecastSum
from 
    (Select 
        SUM(Actual) As ActualSum, 
        SUM (forecast) AS ForecastSum, 
        PAYMENT_SCHEDULE_MASTER_ID
     from 
        [dbo].[PAYMENT_SCHEDULE_DETAIL]
     where Actual=0 and forecast=0
     group by 
        PAYMENT_SCHEDULE_MASTER_ID) t
Inner Join 
    dbo.PAYMENT_SCHEDULE_MASTER psm on psm.PAYMENT_SCHEDULE_MASTER_ID = t.PAYMENT_SCHEDULE_MASTER_ID

话虽如此,如果性能是一个问题,你不需要SUM实际和预测,因为你总结了零。然后我们可以做得更简单:

Select 
   t.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 0 as ActualSum, 0 as ForecastSum
from
    (Select distinct
        PAYMENT_SCHEDULE_MASTER_ID
     from 
        dbo.PAYMENT_SCHEDULE_DETAIL
     where Actual=0 and forecast=0 ) t
Inner Join 
    dbo.PAYMENT_SCHEDULE_MASTER psm
On psm.PAYMENT_SCHEDULE_MASTER_ID = t.PAYMENT_SCHEDULE_MASTER_ID

我在性能优化方面并不太聪明 - 如果你的子查询产生很多行,那么在加入前选择不同的行,如上所述,可能会更快。但你可以试试这个:

Select distinct
   psd.PAYMENT_SCHEDULE_MASTER_ID, psm.FPI_ID, 0 as ActualSum, 0 as ForecastSum
from
   dbo.PAYMENT_SCHEDULE_DETAIL psd
Inner Join 
   dbo.PAYMENT_SCHEDULE_MASTER psm
On
   psm.PAYMENT_SCHEDULE_MASTER_ID = psd.PAYMENT_SCHEDULE_MASTER_ID
Where
   Actual=0 and forecast=0

哪个更简单,但在过滤不同的行之前加入行可能会更慢。并且,不要忘记将FK编入索引,除非您正在进行大量数据写入。

答案 5 :(得分:0)

我相信这就是你要找的东西:

SELECT M.*
FROM Payment_Schedule_Detail D
JOIN Payment_Schedule_Master M
ON M.PAYMENT_SCHEDULE_MASTER_ID = D.PAYMENT_SCHEDULE_MASTER_ID
GROUP BY D.PAYMENT_SCHEDULE_MASTER_ID
HAVING SUM(D.Actual) = 0 AND SUM(D.Forecast) = 0

或者这个:

SELECT M.*
FROM (SELECT PAYMENT_SCHEDULE_MASTER_ID,
             SUM(Actual) ActualSum, SUM(Forecast) ForecastSum
      FROM Payment_Schedule_Detail
      GROUP BY PAYMENT_SCHEDULE_MASTER_ID) D
JOIN Payment_Schedule_Master M
ON M.PAYMENT_SCHEDULE_MASTER_ID = D.PAYMENT_SCHEDULE_MASTER_ID
WHERE ActualSum = 0 AND ForecastSum = 0

不确定哪一个在性能方面更好。我认为第二个会创建一个临时表,因此速度较慢,但​​它可能取决于优化器。

根据您使用的SQL数据库类型,外键可能会或可能不会自动编入索引,如果不是,则应将索引编入索引。

答案 6 :(得分:0)

SELECT psm_id, ActualSum, ForecastSum FROM
(SELECT 
    PAYMENT_SCHEDULE_MASTER_ID AS psm_id, SUM(psd.Actual) As ActualSum, SUM (psd.forecast) AS ForecastSum
FROM 
    [dbo].[PAYMENT_SCHEDULE_DETAIL] psd, dbo.PAYMENT_SCHEDULE_MASTER psm
GROUP BY PAYMENT_SCHEDULE_MASTER_ID
) ts, dbo.PAYMENT_SCHEDULE_MASTER psm1
WHERE psm1.PAYMENT_SCHEDULE_MASTER_ID = ts.psm_id
AND ts.ActualSum = 0 AND ts.ForecastSum
ORDER BY 
     psm1.FPI_ID

答案 7 :(得分:0)

谢谢你的答案,但它根本没有用。

在我的数据库中有一个我没看到的特例。如果特定Payment_Schedule_Id的实际和预测都为0,那么它只有一行。

Select PAYMENT_SCHEDULE_MASTER_ID from (
Select COUNT(*)  As IdCount, PAYMENT_SCHEDULE_MASTER_ID, ACTUAL , FORECAST
from PAYMENT_SCHEDULE_DETAIL
group by PAYMENT_SCHEDULE_MASTER_ID, ACTUAL, FORECAST) t
where t.IdCount = 1 
and t.ACTUAL = 0.00
and t.FORECAST = 0.00

以上是解决方案。