简体   繁体   中英

Combining UNION ALL with GROUP BY

I could really use a little help with this query.

I have a Products table.

CREATE TABLE [dbo].[Products](
    [Id] [int] NOT NULL,
    [Title] [nvarchar](50) NOT NULL,
    -- Additional column omitted
    CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC
    ) ON [PRIMARY]
) ON [PRIMARY]

And a Transactions table.

CREATE TABLE [dbo].[Transactions](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ProductId] [int] NOT NULL,
    [TimeStamp] [datetime] NOT NULL,
    [Quantity] [decimal](9, 3) NOT NULL,
    [TotalAmount] [bigint] NOT NULL,
    -- Additional columns omitted
    CONSTRAINT [PK_Transactions] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC
    ) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[Transactions]  WITH CHECK ADD  CONSTRAINT [FK_Transactions_Products] FOREIGN KEY([ProductId])
REFERENCES [dbo].[Products] ([Id])

And a CCTransactions table.

CREATE TABLE [dbo].[CCTransactions](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ProductId] [int] NOT NULL,
    [TimeStamp] [datetime] NOT NULL,
    [Quantity] [decimal](9, 3) NOT NULL,
    [TotalAmount] [bigint] NOT NULL,
    -- Additional columns omitted
    CONSTRAINT [PK_CCTransactions] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC
    )
) ON [PRIMARY]

ALTER TABLE [dbo].[CCTransactions]  WITH CHECK ADD  CONSTRAINT [FK_CCTransactions_Products] FOREIGN KEY([ProductId])
REFERENCES [dbo].[Products] ([Id])

I need a query that UNION ALL s all the Transactions and CCTransactions that fall within a given date range, and then groups them as Product.Id , Product.Title , SUM(Quantity) , SUM(TotalAmount) .

Below is what I have so far. It won't compile but I'm including because people usually want to see what you've tried. It tells me:

Msg 8120, Level 16, State 1, Line 5
Column 'Products.Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Msg 209, Level 16, State 1, Line 14
Ambiguous column name 'Id'.

I understand the errors, but am not sure the best way to fix them. The query seemed to be working before I added the union.

DECLARE @dtStart DATE = '2016-08-01';
DECLARE @dtEnd DATE = '2016-08-31';

SELECT p.Id, p.Title, SUM(t.Quantity), SUM(t.TotalAmount) AS Amount
FROM Transactions t
INNER JOIN Products p ON t.ProductId = p.Id
WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd
UNION ALL
SELECT p.Id, p.Title, SUM(t.Quantity), SUM(t.TotalAmount) AS Amount
FROM CCTransactions t
INNER JOIN Products p ON t.ProductId = p.Id
WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd
GROUP BY p.Id, p.Title
ORDER BY Title

You need 2 separate GROUP BY s:

SELECT p.Id, p.Title, SUM(t.Quantity), SUM(t.TotalAmount) AS Amount
FROM Transactions t
INNER JOIN Products p ON t.ProductId = p.Id
WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd
GROUP BY p.Id, p.Title

UNION ALL

SELECT p.Id, p.Title, SUM(t.Quantity), SUM(t.TotalAmount) AS Amount
FROM CCTransactions t
INNER JOIN Products p ON t.ProductId = p.Id
WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd
GROUP BY p.Id, p.Title

Or a subquery:

SELECT      X.id, X.Title, SUM(X.Quantity), SUM(X.TotalAmount)
FROM        (
                SELECT p.Id, p.Title, t.Quantity, t.TotalAmount
                FROM Transactions t
                INNER JOIN Products p ON t.ProductId = p.Id
                WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd

                UNION ALL

                SELECT p.Id, p.Title, t.Quantity, t.TotalAmount
                FROM CCTransactions t
                INNER JOIN Products p ON t.ProductId = p.Id
                WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd
            ) X
GROUP BY    X.Id, X.Title

Or you can rewrite the entire query:

;WITH
    AllTransaction AS
    (
        SELECT      ProductID, Timestamp, Quantity, TotalAmount
        FROM        Transaction
        UNION ALL
        SELECT      ProductID, Timestamp, Quantity, TotalAmount
        FROM        CCTransaction
    )

SELECT      p.Id, p.Ttitle, SUM(t.Quantity), SUM(t.TotalAmount)
FROM        AllTransaction      t
INNER JOIN  Products            p   ON t.ProductId = p.Id
WHERE       @dtStart <= t.[Timestamp] AND t.[TimeStamp] < DATEADD(DAY, 1, @dtEnd)
GROUP BY    p.Id, p.Title

A note on CAST(t.[TimeStamp] AS DATE) <= @dtEnd : this condition is not SARGable , meaning index and statistics are of no help. The result of CAST(t.[TimeStamp] AS DATE) is not known until you have applied the function to each and every row. This causes a table scan (ie reading the entire table in) while you may only need a few rows. If Transactions and CCTransactions are huge, you will suffer.

First use UNION ALL for the 2 tables and join the result to Products:

DECLARE @dtStart DATE = '2016-08-01';
DECLARE @dtEnd DATE = '2016-08-31';

SELECT p.Id, p.Title, SUM(t.Quantity), SUM(t.TotalAmount) AS Amount
FROM (
  SELECT ProductId, [TimeStamp], Quantity, TotalAmount FROM Transactions
  UNION ALL
  SELECT ProductId, [TimeStamp], Quantity, TotalAmount FROM CCTransactions
) t INNER JOIN Products p ON t.ProductId = p.Id
WHERE t.[TimeStamp] >= @dtStart AND CAST(t.[TimeStamp] AS DATE) <= @dtEnd
GROUP BY p.Id, p.Title
ORDER BY Title

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM