如何使用连接和类似where子句简化查询

时间:2016-12-15 07:23:53

标签: sql sql-server optimization

我有多个Stored Procedures加入了类似where子句的连接,实际上唯一的区别是FROM表。如果可能的话,我想删除重复。这是一个有效的例子:

SELECT x.Month_Name
 , x.Invoice_Amt
 , y.Payment_Amt
FROM
(
    SELECT d.Month_Name
         , d.Month
         , sum(i.Amount) AS Invoice_Amt
    FROM Invoice i
         INNER JOIN DATE d ON i.Trans_Date = d.PK_Date
    WHERE d.Month BETWEEN '2016-01-01' AND '2016-02-01'
          AND i.Account = '1-AAA'
    GROUP BY d.Month
           , d.Month_Name
) x
JOIN
(
    SELECT d.Month_Name
         , sum(p.Amount) AS Payment_Amt
    FROM Payment p
         INNER JOIN DATE d ON p.Payment_Date = d.PK_Date
    WHERE d.Month BETWEEN '2016-01-01' AND '2016-02-01'
          AND p.Account = '1-AAA'
    GROUP BY d.month
           , d.month_Name
) y ON x.Month_Name = y.Month_Name
ORDER BY x.Month
       , x.Month_Name;

这是我的目标的一个非工作的例子:

SELECT d.Month_Name
     , d.Month
     , sum(i.Amount) AS Invoice_Amt
     , sum(p.Amount) AS Payment_Amt
FROM Invoice i
     INNER JOIN DATE d ON i.Trans_Date = d.PK_Date
     JOIN Payment p ON i.Account = p.Account
WHERE d.Month BETWEEN '2016-01-01' AND '2016-02-01'
      AND i.Account = '1-AAA'
GROUP BY d.Month
       , d.Month_Name
ORDER BY d.Month
       , d.Month_Name;

我无法让SQL Fiddle工作的数据库模式。

CREATE TABLE Date
(PK_Date    datetime,
 Month      datetime,
 Month_Name varchar(20));

INSERT INTO Date
VALUES
('2016-01-01', '2016-01-01', 'January 2016'),
('2016-01-15', '2016-01-01', 'January 2016'),
('2016-02-04', '2016-02-01', 'February 2016'),
('2016-02-15', '2016-02-01', 'February 2016');

CREATE TABLE Invoice
(Account    varchar(15),
Trans_Date datetime,
Amount     float);

INSERT INTO Invoice
VALUES
('1-AAA', '2016-01-01', 521.32),
('1-AAA', '2016-02-04', 854.20);

CREATE TABLE Payment
(Account      varchar(15),
Payment_Date datetime,
Amount       float);

INSERT INTO Payment
VALUES
('1-AAA', '2016-01-15', 410.50), 
('1-AAA', '2016-02-15', 120.98);

2 个答案:

答案 0 :(得分:2)

我会使用[DATE]作为基本表并离开外部联接[Invoice]& [付款]。 像这样:

SELECT d.Month_Name
     , d.Month
     , sum(i.Amount) AS Invoice_Amt
     , sum(p.Amount) AS Payment_Amt
FROM DATE d 
LEFT OUTER JOIN Invoice i ON i.Trans_Date = d.PK_Date
LEFT OUTER JOIN Payment p ON p.Payment_Date = d.PK_Date
WHERE d.Month BETWEEN '2016-01-01' AND '2016-02-01'
      AND (i.Account = '1-AAA' OR p.Account = '1-AAA')
GROUP BY d.Month
       , d.Month_Name
ORDER BY d.Month
       , d.Month_Name;

答案 1 :(得分:0)

您根本不需要[日期]表。 Updated query >>

DECLARE @dt_from	DATETIME		=	'2016-01-01';
DECLARE @dt_to		DATETIME		=	'2016-02-01';
DECLARE @acc		VARCHAR(256)	=	'1-AAA';


SELECT
	 [Month_Name]	=	DATENAME(MONTH, [agg_data].[dt]) + SPACE(1) + CAST(DATEPART(YEAR, [agg_data].[dt]) AS VARCHAR(4))
--	,[Month]		=	[agg_data].[dt]
	,[Invoice_Amt]	=	[agg_data].[amount_inv]
	,[Payment_Amt]	=	[agg_data].[amount_pmt]
FROM
	(
		SELECT
			  [dt]			=	ISNULL([i].[dt], [p].[dt])     
			 ,[amount_inv]	=	ISNULL([i].[amount], 0)
			 ,[amount_pmt]	=	ISNULL([p].[amount], 0)
			 ,[acc]			=	ISNULL([i].[acc], [p].[acc])
		FROM
			(
				SELECT
					 [acc]		=	[account]		
					,[dt]		=	DATEFROMPARTS(YEAR([trans_date]), MONTH([trans_date]), 1)
					,[amount]	=	SUM([amount])
				FROM
					[invoice]
				GROUP BY
					 DATEFROMPARTS(YEAR([trans_date]), MONTH([trans_date]), 1)
					,[account]
			)	AS	[i]
		FULL JOIN
			( 
				SELECT
					 [acc]		=	[account]		
					,[dt]		=	DATEFROMPARTS(YEAR([payment_date]), MONTH([payment_date]), 1)
					,[amount]	=	SUM([amount])
				FROM
					[payment]
				GROUP BY
					 DATEFROMPARTS(YEAR([payment_date]), MONTH([payment_date]), 1)
					,[account]
			)	AS	[p]
		ON
				[i].[acc]	=	[p].[acc]
			AND	[i].[dt]	=	[p].[dt]
	)	AS	[agg_data]
WHERE
		[dt]	BETWEEN @dt_from AND @dt_to
	AND	[acc]	=		@acc
ORDER BY
	 [dt] ASC;