Add dimensions to denormalized concatenated columns queried from temporary tables - sql

I am querying two tables from an oracle database. POS.SNAPSHOT_SUMMARY_LOG contains daily snapshots of all reserved orders from today into the future. POS.SALES_SUMMARY_LOG contains final sales history until yesterday. I am trying to create a denormalized table that has a column with sales figures at different points in time (i.e. WINDOWS). The query below works well and is fast. I get a column for each sum for each window. See results for 2/26/18-3/26/18 windows_query. (The PREVIOUS_YEAR__DATE column is the most similar weekday from the previous year and not the same calendar date of the previous year)
However, I can't figure out how to group by the dimensions of PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL to the results so that the query runs quickly.
WITH SNAP AS( SELECT
TRUNC(SYSDATE,'DDD')-SNAPSHOT_DATE AS WINDOW, CURRENT_YEAR_DATE, PREVIOUS_YEAR__DATE, PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL, QUANTITY, REVENUE
FROM POS.SNAPSHOT_SUMMARY_LOG
WHERE (SNAPSHOT_DATE IN (TRUNC(SYSDATE,'DDD'), TRUNC(SYSDATE,'DDD')-7, TRUNC(SYSDATE,'DDD')-365,TRUNC(SYSDATE,'DDD')-372))),
HIST AS (SELECT CURRENT_YEAR_DATE, PREVIOUS_YEAR__DATE, PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL, QUANTITY, REVENUE
FROM POS.SALES_SUMMARY_LOG)
SELECT DISTINCT SNAP.CURRENT_YEAR_DATE, SNAP.PREVIOUS_YEAR__DATE,
CONCAT(SNAP0.REVENUE,HIST0.REVENUE)+0 AS REVENUE0,
CONCAT(SNAP7.REVENUE,HIST7.REVENUE)+0 AS REVENUE7,
CONCAT(SNAP365.REVENUE,HIST365.REVENUE)+0 AS REVENUE365,
CONCAT(SNAP372.REVENUE,HIST372.REVENUE)+0 AS REVENUE372,
CONCAT(SNAP0.QUANTITY,HIST0.QUANTITY)+0 AS QUANTITY0,
CONCAT(SNAP7.QUANTITY,HIST7.QUANTITY)+0 AS QUANTITY7,
CONCAT(SNAP365.QUANTITY,HIST365.QUANTITY)+0 AS QUANTITY365,
CONCAT(SNAP372.QUANTITY,HIST372.QUANTITY)+0 AS QUANTITY372
FROM SNAP
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=0 GROUP BY CURRENT_YEAR_DATE) SNAP0 ON SNAP.CURRENT_YEAR_DATE=SNAP0.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=7 GROUP BY CURRENT_YEAR_DATE) SNAP7 ON SNAP.CURRENT_YEAR_DATE=SNAP7.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=365 GROUP BY CURRENT_YEAR_DATE ) SNAP365 ON SNAP.PREVIOUS_YEAR_DATE=SNAP365.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM SNAP WHERE WINDOW=372 GROUP BY CURRENT_YEAR_DATE) SNAP372 ON SNAP.PREVIOUS_YEAR_DATE=SNAP372.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-0 GROUP BY CURRENT_YEAR_DATE) HIST0 ON SNAP.CURRENT_YEAR_DATE=HIST0.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-7 GROUP BY CURRENT_YEAR_DATE) HIST7 ON SNAP.CURRENT_YEAR_DATE=HIST7.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-365 GROUP BY CURRENT_YEAR_DATE) HIST365 ON SNAP.PREVIOUS_YEAR_DATE=HIST365.CURRENT_YEAR_DATE
LEFT OUTER JOIN (SELECT CURRENT_YEAR_DATE, sum(REVENUE) AS REVENUE, sum(QUANTITY) AS QUANTITY FROM HIST WHERE CURRENT_YEAR_DATE<TRUNC(SYSDATE,'DDD')-372 GROUP BY CURRENT_YEAR_DATE) HIST372 ON SNAP.PREVIOUS_YEAR_DATE=HIST372.CURRENT_YEAR_DATE
WHERE SNAP.CURRENT_YEAR_DATE BETWEEN TRUNC(SysDate,'YEAR') AND ADD_MONTHS(TRUNC(TRUNC(SysDate,'YEAR'),'Y'),15)

I think you can rewrite your query using conditional aggregation, which ought to be a lot more performant than your current query.
E.g. I think you could rewrite it to be something like:
SELECT current_year_date,
previous_year__date,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 0 THEN revenue END) snap_revenue0,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 7 THEN revenue END) snap_revenue7,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 365 THEN revenue END) snap_revenue365,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 372 THEN revenue END) snap_revenue372,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 0 THEN revenue END) hist_revenue0,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 7 THEN revenue END) hist_revenue7,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 365 THEN revenue END) hist_revenue365,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 372 THEN revenue END) hist_revenue372,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 0 THEN quantity END) snap_quantity0,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 7 THEN quantity END) snap_quantity7,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 365 THEN quantity END) snap_quantity365,
SUM(CASE WHEN trunc(sysdate) - snapshot_date = 372 THEN quantity END) snap_quantity372,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 0 THEN quantity END) hist_quantity0,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 7 THEN quantity END) hist_quantity7,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 365 THEN quantity END) hist_quantity365,
SUM(CASE WHEN CURRENT_YEAR_DATE < (trunc(sysdate) - 372 THEN quantity END) hist_quantity372
FROM pos.snapshot_summary_log
GROUP BY current_year_date,
previous_year__date;
I don't quite understand what you mean by "add the dimensions of PRODUCT_CATEGORY, CUSTOMER_CATEGORY, SALES_CHANNEL"; if you mean you want the results grouped with those additional columns, then it's simply a matter of adding them into the above select column and group by lists, e.g.:
SELECT current_year_date,
previous_year__date,
product_category,
customer_category,
sales_channel,
<SUM columns>
FROM pos.snapshot_summary_log
GROUP BY current_year_date,
previous_year__date,
product_category,
customer_category,
sales_channel;
I've not done anything with the columns; you may want to throw an outer query around this that adds the snap and hist columns together, or maybe COALESCEs them, but I'll leave that up to you to decide.

Related

BigQuery: group counters by month after self-join

I have table that looks like this:
I'm trying to build a query, that will show specific partnerId counters groupped by keywordName and month.
To solve first part(without grouping by month), I've built this query:
SELECT keywordName, COUNT(keywordName) as total, IFNULL(b.ebay_count, 0) as ebay, IFNULL(c.amazon_count, 0) as amazon,
FROM LogFilesv2_Dataset.FR_Clickstats_v2 a
LEFT JOIN
(SELECT keywordName as kw , SUM(CASE WHEN partnerId='eBay' THEN 1 ELSE 0 END) as ebay_count
FROM LogFilesv2_Dataset.FR_Clickstats_v2
WHERE partnerId = 'eBay' GROUP BY kw) b
ON keywordName = b.kw
LEFT JOIN
(SELECT keywordName as kw , SUM(CASE WHEN partnerId='AmazonApi' THEN 1 ELSE 0 END) as amazon_count
FROM LogFilesv2_Dataset.FR_Clickstats_v2
WHERE partnerId = 'AmazonApi' GROUP BY kw) c
ON keywordName = c.kw
WHERE keywordName = 'flipper' -- just to filter out single kw.
GROUP BY keywordName, ebay, amazon
It works quite well and returns following output:
Now I'm trying to make additional group by month, but all my attempts returned incorrect results.
Final output supposed to be similar to this:
You can do this with conditional aggregation:
select
date_trunc(dt, month) dt,
keywordName,
count(*) total,
sum(case when partnerId = 'eBay' then 1 else 0 end) ebay,
sum(case when partnerId = 'AmazonApi' then 1 else 0 end) amazon
from LogFilesv2_Dataset.FR_Clickstats_v2
group by date_trun(dt, month), keywordName

Complex SQL query with aggregate functions

I have 3 tables
Sales journal SALES:
DATE - Sale date
T_CODE - Product code
QUAN - Quantity sold
Product journal PRODUCTS:
CODE - Product code
NAME - Product name
Prices journal PRICES:
T_CODE - Product code
DATE - Date of change of price(i.e. the changed price is valid from that date onwards till the next change of price)
COST - The price of the product
I need to summarize total quantity sold and total value sold for first three months of 2018
I've tried to construct SQL query for this as follows:
SELECT PRODUCTS.NAME, PRODUCT.T_CODE
(SELECT SUM(SALES.QUAN) WHERE SALES.DATE BETWEEN '01.01.2018' AND '31.01.2018') AS JANUARY_QUANTITY
(SELECT SUM(SALES.QUAN)*PRICES.COST FROM SALES INNER JOIN PRICES ON PRICES.T_CODE = SALES.T_CODE) AS JANUARY_VALUE
(SELECT SUM(SALES.QUAN) WHERE SALES.DATE BETWEEN '01.02.2018' AND '28.02.2018') AS FEBRUARY_QUANTITY
(SELECT SUM(SALES.QUAN)*PRICES.COST FROM SALES INNER JOIN PRICES ON PRICES.T_CODE = SALES.T_CODE) AS FEBRUARY_VALUE
(SELECT SUM(SALES.QUAN) WHERE SALES.DATE BETWEEN '01.03.2018' AND '31.03.2018') AS MARCH_QUANTITY
(SELECT SUM(SALES.QUAN)*PRICES.COST FROM SALES INNER JOIN PRICES ON PRICES.T_CODE = SALES.T_CODE) AS MARCH_VALUE
LEFT JOIN PRODUCTS.CODE
GROUP BY ST.NAME;
Please help me to construct correct SQL query for this.
SELECT SUM(SALES.QUAN)*PRICES.COST FROM SALES INNER JOIN PRICES ON PRICES.T_CODE = SALES.T_CODE) AS JANUARY_VALUE
You are not getting the output that you are missing a JOIN condition on that matches the date of the sale with the date of the price. Also, you would need to move the multiplication into the aggregate function.
Putting the price inside the aggregate function allows the calculation to work correctly.
Also, I believe that your query could probably be simplified by using conditional aggregation, as follows :
SELECT
SUM(CASE WHEN S.DATE BETWEEN '01.01.2018' AND '31.01.2018' THEN S.QUAN ELSE 0 END) AS JANUARY_QUANTITY,
SUM(CASE WHEN S.DATE BETWEEN '01.01.2018' AND '31.01.2018' THEN S.QUAN * P.COST ELSE 0 END) AS JANUARY_VALUE,
SUM(CASE WHEN S.DATE BETWEEN '01.02.2018' AND '28.02.2018' THEN S.QUAN ELSE 0 END) AS FEBRUARY_QUANTITY,
SUM(CASE WHEN S.DATE BETWEEN '01.02.2018' AND '28.02.2018' THEN S.QUAN * P.COST ELSE 0 END) AS FEBRUARY_VALUE,
SUM(CASE WHEN S.DATE BETWEEN '01.03.2018' AND '31.03.2018' THEN S.QUAN ELSE 0 END) AS MARCH_QUANTITY,
SUM(CASE WHEN S.DATE BETWEEN '01.03.2018' AND '31.03.2018' THEN S.QUAN * P.COST ELSE 0 END) AS MARCH_VALUE
FROM
SALES S
LEFT JOIN PRICES P ON P.T_CODE = S.T_CODE AND P.DATE = S.DATE
With this technique, the table is scanned only once, and the results are then analyzed to feed the relevant columns in the resultset. This should efficiently replace your 6 subqueries.

Teradata error "Selected non-aggregate values must be part of the associated group"

>SELECT sku, jun_sale, jul_sale, aug_sale, (jun_sale + jul_sale + aug_sale)
>AS total_sale,
>CASE
>when EXTRACT(MONTH from saledate)=6 AND stype= 'P'
>then sum(amt)
>end as jun_sale,
>CASE
>when EXTRACT(MONTH from saledate)=7 AND stype= 'P'
>then sum(amt)
>end as jul_sale,
>CASE
>when EXTRACT(MONTH from saledate)=8 AND stype= 'P'
>then sum(amt)
>end as aug_sale
>FROM trnsact
>group by 1
>order by total_sale;
Selected non-aggregate values must be part of the associated group
Can someone find out what is the error in the case?
You probably want to SUM over CASE:
select sku,
sum(case
when EXTRACT(MONTH from saledate)=6 AND stype= 'P'
then amt
end) as jun_sale,
sum(case
when EXTRACT(MONTH from saledate)=7 AND stype= 'P'
then amt
end) as jul_sale,
sum(case
when EXTRACT(MONTH from saledate)=8 AND stype= 'P'
then amt
end) as aug_sale,
jun_sale + jul_sale + aug_sale as total_sale
from trnsact
group by 1
order by total_sale;
And you should add a WHERE-condition restricting to rows from June to August.
Edit:
If an item was not sold in any of the three months you will get a NULL (and then the total_sale calculation will also result in NULL). You can add else 0 to each CASE to get zeroes instead.

Oracle monthly totals and grand total column and row

I'm trying to generate a report using Oracle which looks like this:
I got the following tables:
REGION (IDRegion, Region_Name)
SALES (IDSale, Sale_Date, Amount, IDSaleRegion)
Both tables are populated.
I cannot figure out how can I create that report by extracting each sum for the first 3 months and calculate the totals.
I've only made this script but it's not so useful:
SELECT Region_Name as Region,
COALESCE(NULL, NULL) AS "January",
COALESCE(NULL, NULL)AS"February",
COALESCE(NULL, NULL)AS"March",
COALESCE(NULL, NULL)AS"GrandTotal"
FROM REGION r INNER JOIN SALES s ON r.IDRegion=s.IDSaleRegion
GROUP BY Region_Name;
EDIT: Problem solved except the TOTAL (below Region) which calculates the totals from each Month. Any ideas how to add this row?
You can use Conditional Aggregation
select case grouping(Region_Name) when 1 then 'TOTAL' else Region_Name as "Region",
sum(case when extract(month from Sale_Date) = 1 then Amount else 0 end) AS "January",
sum(case when extract(month from Sale_Date) = 2 then Amount else 0 end) AS "February",
sum(case when extract(month from Sale_Date) = 3 then Amount else 0 end) AS "March",
sum(Amount) AS"GrandTotal"
From yourtable
Where extract(month from Sale_Date) <= 3
Group by Rollup (Region_Name);
try something like this:
SELECT Region_Name as Region,
sum(decode(extract(month from Sale_Date), 1, amount)) AS "January",
sum(decode(extract(month from Sale_Date), 2, amount)) AS"February",
sum(decode(extract(month from Sale_Date), 3, amount)) AS"March",
sum(case when extract(month from Sale_Date)<4 then amount end) AS"GrandTotal"
FROM REGION r INNER JOIN SALES s ON r.IDRegion=s.IDSaleRegion
GROUP BY Region_Name;

Subquery returned more than 1 value in MS SQL

In MS Sql.
SELECT a.SellerID,
SUM(TransactionFee) as TransactionFees,
SUM(Quantity*a.PriceItem) as TransactionValue,
COUNT(*) as OrdersWithTransactionFees,
SUM(Quantity) as Qty,
(SELECT SUM(a.Quantity*a.PriceItem) as WholeMonthTransactionValue
from BuyProductDetails where SellerID = a.SellerID) as aa
FROM BuyProductDetails as a
WHERE MONTH(a.OrderDate)=3
AND YEAR(a.OrderDate)=2013
AND TransactionFee IS NOT NULL
GROUP BY a.SellerID
I have the above query... it can't seems to be able to run.
Basically, I have this table BuyProductDetails which stores all the orders from different Sellers.
Some orders will have TransactionFee.
Now, what I need is to calculate the total sales of these orders with TransactionFee, and the total sales for these sellers including those orders without TransactionFee.
The result set should have the following fields:
SellerID
Sum of Transaction fee
Sum of total sales
Number of Orders with Transaction fee
Qty ordered
Total sales for that seller
But when I run this sql, it returns the following error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Any help is much appreciated. Thank you.
Tried something like this ?
SELECT a.SellerID,
SUM(TransactionFee) as TransactionFees,
SUM(Quantity*a.PriceItem) as TransactionValue,
COUNT(*) as OrdersWithTransactionFees,
SUM(Quantity) as Qty,
MIN(a.WholeMonthTransactionValue) as WholeMonthTransactionValue
FROM BuyProductDetails as a,
(SELECT b.SellerID,
SUM(b.Quantity*b.PriceItem) as WholeMonthTransactionValue,
MONTH(b.OrderDate),
YEAR(b.OrderDate)
FROM BuyProductDetails b
GROUP BY b.SellerID,
MONTH(b.OrderDate) as MonthID,
YEAR(b.OrderDate) as YearID) as aa
WHERE MONTH(a.OrderDate)=3
AND YEAR(a.OrderDate)=2013
AND TransactionFee IS NOT NULL
AND a.SellerID = aa.SellerID
AND MONTH(a.OrderDate)=aa.MonthID
AND YEAR(a.OrderDate) = aa.YearID
GROUP BY a.SellerID)
You can use more effective option with CASE expression
SELECT a.SellerID,
SUM(CASE WHEN TransactionFee IS NOT NULL THEN TransactionFee END) AS TransactionFees,
SUM(CASE WHEN TransactionFee IS NOT NULL THEN Quantity * PriceItem END) AS TransactionValue,
COUNT(CASE WHEN TransactionFee IS NOT NULL THEN 1 END) as OrdersWithTransactionFees,
SUM(CASE WHEN TransactionFee IS NOT NULL THEN Quantity END) as Qty,
SUM(Quantity * PriceItem) AS WholeMonthTransactionValue
FROM BuyProductDetails AS a
WHERE MONTH(a.OrderDate) = 3 AND YEAR(a.OrderDate) = 2013
GROUP BY a.SellerID
Demo on SQLFiddle
Or merely add correct alias in the subquery
SELECT a.SellerID,
SUM(TransactionFee) as TransactionFees,
SUM(Quantity*a.PriceItem) as TransactionValue,
COUNT(*) as OrdersWithTransactionFees,
SUM(Quantity) as Qty,
(SELECT SUM(d.Quantity * d.PriceItem)
FROM BuyProductDetails d
WHERE d.SellerID = a.SellerID) as WholeMonthTransactionValue
FROM BuyProductDetails as a
WHERE MONTH(a.OrderDate)=3
AND YEAR(a.OrderDate)=2013
AND TransactionFee IS NOT NULL
GROUP BY a.SellerID
Demo on SQLFiddle