PRINT only SUM by year (group by Territory) in SQL - sql

SELECT year(soh.OrderDate) 'year',sum(soh.TotalDue) 'Total',st.[Group] TerritoryGroup
FROM Sales.SalesOrderHeader soh
LEFT OUTER JOIN Sales.SalesTerritory st
ON soh.TerritoryID=st.TerritoryID
GROUP BY year(soh.OrderDate),(soh.TotalDue),[Group]
ORDER BY year(soh.OrderDate),(soh.TotalDue)
This is what I came up with, but the years are scattered instead of ONE year per Territory total.
(I like to print the Total for each year in each Territory)
Is there a concise way to make this select statement?

If you want one row per year, then only include that in the group by:
SELECT year(soh.OrderDate) as year, sum(soh.TotalDue) as Total
FROM Sales.SalesOrderHeader soh LEFT OUTER JOIN
Sales.SalesTerritory st
ON soh.TerritoryID = st.TerritoryID
GROUP BY year(soh.OrderDate)
ORDER BY year(soh.OrderDate);
If you want one row per year and territory group, then include only those two columns:
SELECT year(soh.OrderDate) as year, sum(soh.TotalDue) as Total, st.[Group] as TerritoryGroup
FROM Sales.SalesOrderHeader soh LEFT OUTER JOIN
Sales.SalesTerritory st
ON soh.TerritoryID = st.TerritoryID
GROUP BY year(soh.OrderDate), [Group]
ORDER BY year(soh.OrderDate), Total;
Some notes:
You do not need single quotes around the column aliases. You should use single quotes only for string and date constants.
If you are summarizing just by year, then you cannot have TerritoryGroup in the output.
In neither case would you include soh.TotalDue in the group by. You are summing that column, not aggregating by it.
The order by clause should not contain soh.TotalDue; it should be the aggregated value (Total) instead.

In GROUP BY you say you want one line per combination of year, totaldue and territory group.
Let's say you have these records:
orderdate totaldue territorygroup
2014-01-01 100 1
2014-01-15 200 1
2014-01-21 100 1
2013-03-03 100 1
2014-04-04 100 2
Then you get these result records:
year totaldue territorygroup
2014 100 1
2014 200 1
2013 100 1
2014 100 2
(BTW: sum(soh.TotalDue) = soh.TotalDue, because you group by TotalDue.)
So the solution for you is to say what you want to see in your result records actually. One record per ______. Thus you get your GROUP BY clause and the results you want.

Related

How to add SUM columns to a SQL query that are totaled by week?

I'm writing a query to break down quantity of total transactions by week happening from 8pm to 3am. Here is what I'm trying to accomplish:
StoreNo
Week 1
000001
123
000002
123
(Week 2, Week 3,...)
I am also trying to pull from multiple tables, which are the following:
StoreNo: align_dim
Week No: time_day_dim
Transaction Count: tld.fact_v1
The query I have so far is:
SELECT a.restid,
COUNT(DISTINCT tld_fact_v1.dw_gc_header) as "Total Transactions"
FROM tbc.tbcdbv.tld_fact_v1
LEFT JOIN tbcdb.align_dim a ON a.dw_restid=tbc.tbcdbv.tld_fact_v1.dw_restid
LEFT JOIN tbcdbv.time_day_dim_v1 on tld_fact_v1.dw_day=time_day_dim_v1.dw_day
WHERE time_day_dim_v1.fiscalyearno = 'Y2022'
GROUP BY 1
This query works and I receive:
StoreNo
Total
000001
123
000002
123
How would I be able to get it split out by week?
UNION the result of all count for year 2022
SELECT a.restid,
COUNT(DISTINCT tld_fact_v1.dw_gc_header) as "Total Transactions"
FROM tbc.tbcdbv.tld_fact_v1
LEFT JOIN tbcdb.align_dim a ON a.dw_restid=tbc.tbcdbv.tld_fact_v1.dw_restid
LEFT JOIN tbcdbv.time_day_dim_v1 on tld_fact_v1.dw_day=time_day_dim_v1.dw_day
WHERE time_day_dim_v1.fiscalyearno = 'Y2022'
GROUP BY 1
UNION
SELECT NULL,
COUNT(DISTINCT tld_fact_v1.dw_gc_header) as "Total Transactions"
FROM tbc.tbcdbv.tld_fact_v1
LEFT JOIN tbcdb.align_dim a ON a.dw_restid=tbc.tbcdbv.tld_fact_v1.dw_restid
LEFT JOIN tbcdbv.time_day_dim_v1 on tld_fact_v1.dw_day=time_day_dim_v1.dw_day
WHERE time_day_dim_v1.fiscalyearno = 'Y2022'

Somar um valor total e retornar muitas linhas

I have two tables (order header and order lines). I want to add the order lines, group by month and year, but it brings me all the rows of all the orders. Have you any way to sum it up?
This is probably because I'm doing one calculation per line.
I need to do it per line because the stock is from a few different places.
For example, one stock is from the back street and another stock is from the other block.
If I use the total discount for the sales order, it doubles the same order if the inventory comes from different places.
and if I use the calculation to make the discount per line, it will bring line by line (for sure).
Was there any other possibility?
I tried with variable, but I can not pass more than one value on the same variable.
SELECT
MONTH(X.DOCDATE) MES,
YEAR(X.DOCDATE) ANO,
(100 - X.DiscPrcnt)* SUM(LineTotal) /100 as 'TOTAL'
FROM RDR1 INNER JOIN ORDR X ON RDR1.DocEntry = X.DocEntry
WHERE X.CANCELED <> 'Y'
AND X.DocTotal > 0
AND X.DocDate BETWEEN '20140101' AND '20190630'
AND OcrCode IN ('EXT', 'EXT-JD')
Group by
X.DOCDATE,
X.DiscPrcnt
ORDER BY X.DOCDATE
I would like it to be:
Month YEAR Total
1 2014 5000
2 2014 7000
I imagine you want to be grouping by month and year rather than the full date. Also, I think you may not want a separate row for each discount percent value right?
How about this query?:-
SELECT
MONTH(X.DOCDATE) MES,
YEAR(X.DOCDATE) ANO,
SUM((100 - X.DiscPrcnt)* X.LineTotal/100) as 'TOTAL'
FROM RDR1 INNER JOIN ORDR X ON RDR1.DocEntry = X.DocEntry
WHERE X.CANCELED <> 'Y'
AND X.DocTotal > 0
AND X.DocDate BETWEEN '20140101' AND '20190630'
AND OcrCode IN ('EXT', 'EXT-JD')
Group by
YEAR(X.DOCDATE),
MONTH(X.DOCDATE)
ORDER BY
YEAR(X.DOCDATE),
MONTH(X.DOCDATE)

How to do a group by without having to pass all the columns from the select?

I have the following select, whose goal is to select all customers who had no sales since the day X, and also bringing the date of the last sale and the number of the sale:
select s.customerId, s.saleId, max (s.date) from sales s
group by s.customerId, s.saleId
having max(s.date) <= '05-16-2013'
This way it brings me the following:
19 | 300 | 26/09/2005
19 | 356 | 29/09/2005
27 | 842 | 10/05/2012
In another words, the first 2 lines are from the same customer (id 19), I wish to get only one record for each client, which would be the record with the max date, in the case, the second record from this list.
By that logic, I should take off s.saleId from the "group by" clause, but if I do, of course, I get the error:
Invalid expression in the select list (not contained in either an
aggregate function or the GROUP BY clause)
I'm using Firebird 1.5
How can I do this?
GROUP BY summarizes data by aggregating a group of rows, returning one row per group. You're using the aggregate function max(), which will return the maximum value from one column for a group of rows.
Let's look at some data. I renamed the column you called "date".
create table sales (
customerId integer not null,
saleId integer not null,
saledate date not null
);
insert into sales values
(1, 10, '2013-05-13'),
(1, 11, '2013-05-14'),
(1, 12, '2013-05-14'),
(1, 13, '2013-05-17'),
(2, 20, '2013-05-11'),
(2, 21, '2013-05-16'),
(2, 31, '2013-05-17'),
(2, 32, '2013-03-01'),
(3, 33, '2013-05-14'),
(3, 35, '2013-05-14');
You said
In another words, the first 2 lines are from the same customer(id 19), i wish he'd get only one record for each client, which would be the record with the max date, in the case, the second record from this list.
select s.customerId, max (s.saledate)
from sales s
where s.saledate <= '2013-05-16'
group by s.customerId
order by customerId;
customerId max
--
1 2013-05-14
2 2013-05-16
3 2013-05-14
What does that table mean? It means that the latest date on or before May 16 on which customer "1" bought something was May 14; the latest date on or before May 16 on which customer "2" bought something was May 16. If you use this derived table in joins, it will return predictable results with consistent meaning.
Now let's look at a slightly different query. MySQL permits this syntax, and returns the result set below.
select s.customerId, s.saleId, max(s.saledate) max_sale
from sales s
where s.saledate <= '2013-05-16'
group by s.customerId
order by customerId;
customerId saleId max_sale
--
1 10 2013-05-14
2 20 2013-05-16
3 33 2013-05-14
The sale with ID "10" didn't happen on May 14; it happened on May 13. This query has produced a falsehood. Joining this derived table with the table of sales transactions will compound the error.
That's why Firebird correctly raises an error. The solution is to drop saleId from the SELECT clause.
Now, having said all that, you can find the customers who have had no sales since May 16 like this.
select distinct customerId from sales
where customerID not in
(select customerId
from sales
where saledate >= '2013-05-16')
And you can get the right customerId and the "right" saleId like this. (I say "right" saleId, because there could be more than one on the day in question. I just chose the max.)
select sales.customerId, sales.saledate, max(saleId)
from sales
inner join (select customerId, max(saledate) max_date
from sales
where saledate < '2013-05-16'
group by customerId) max_dates
on sales.customerId = max_dates.customerId
and sales.saledate = max_dates.max_date
inner join (select distinct customerId
from sales
where customerID not in
(select customerId
from sales
where saledate >= '2013-05-16')) no_sales
on sales.customerId = no_sales.customerId
group by sales.customerId, sales.saledate
Personally, I find common table expressions make it easier for me to read SQL statements like that without getting lost in the SELECTs.
with no_sales as (
select distinct customerId
from sales
where customerID not in
(select customerId
from sales
where saledate >= '2013-05-16')
),
max_dates as (
select customerId, max(saledate) max_date
from sales
where saledate < '2013-05-16'
group by customerId
)
select sales.customerId, sales.saledate, max(saleId)
from sales
inner join max_dates
on sales.customerId = max_dates.customerId
and sales.saledate = max_dates.max_date
inner join no_sales
on sales.customerId = no_sales.customerId
group by sales.customerId, sales.saledate
then you can use following query ..
EDIT changes made after comment by likeitlikeit for only one row per CustomerID even when we will have one case where we have multiple saleID for customer with certain condition -
select x.customerID, max(x.saleID), max(x.x_date) from (
select s.customerId, s.saleId, max (s.date) x_date from sales s
group by s.customerId, s.saleId
having max(s.date) <= '05-16-2013'
and max(s.date) = ( select max(s1.date)
from sales s1
where s1.customeId = s.customerId))x
group by x.customerID
You can Try Maxing the s.saleId (Max(s.saleId)) and removing it from the Group By clause
A subquery should do the job, I can't test it right now but it seems ok:
SELECT s.customerId, s.saleId, subq.maxdate
FROM sales AS s
INNER JOIN (SELECT customerId, MAX(date) AS maxdate
FROM sales
GROUP BY customerId, saleId
HAVING MAX(s.date) <= '05-16-2013'
) AS subq
ON s.customerId = subq.customerId AND s.date = subq.maxdate

TERADATA: Aggregate across multiple tables

Consider the following query where aggregation happens across two tables: Sales and Promo and the aggregate values are again used in a calculation.
SELECT
sales.article_id,
avg((sales.euro_value - ZEROIFNULL(promo.euro_value)) / NULLIFZERO(sales.qty - ZEROIFNULL(promo.qty)))
FROM
( SELECT
sales.article_id,
sum(sales.euro_value),
sum(sales.qty)
from SALES_TABLE sales
where year >= 2011
group by article_id
) sales
LEFT OUTER JOIN
( SELECT
promo.article_id,
sum(promo.euro_value),
sum(promo.qty)
from PROMOTION_TABLE promo
where year >= 2011
group by article_id
) promo
ON sales.article_id = promo.article_id
GROUP BY sales.article_id;
Some notes on the query:
Both the inner queries return huge number of rows due to large number of articles. Running explain on teradata, the inner queries themselves take very less time, but the join takes a long time.
Assume primary key on article_id is present and both the tables are partitioned by year.
Left Outer Join because second table contains optional data.
So, can you suggest a better way of writing this query. Thanks for reading this far :)
Not really sure how the avg function got into the mix, so I'm removing it.
SELECT article_id,
(SUM(sales_value) - SUM(promo_value)) /
(SUM(sales_qty) - SUM(promo_qty))
FROM (
SELECT
article_id,
sum(euro_value) AS sales_value,
sum(qty) AS sales_qty,
0 AS promo_value,
0 AS promo_qty
from SALES_TABLE sales
where year >= 2011
group by article_id
UNION ALL
SELECT
article_id,
0 AS sales_value,
0 AS sales_qty,
sum(euro_value) AS promo_value,
sum(qty) AS promo_qty
from SALES_TABLE sales
where year >= 2011
group by article_id
) AS comb
GROUP BY article_id;

SQL query to identify seasonal sales items

I need a SQL query that will identify seasonal sales items.
My table has the following structure -
ProdId WeekEnd Sales
234 23/04/09 543.23
234 30/04/09 12.43
432 23/04/09 0.00
etc
I need a SQL query that will return all ProdId's that have 26 weeks consecutive 0 sales. I am running SQL server 2005. Many thanks!
Update: A colleague has suggested a solution using rank() - I'm looking at it now...
Here's my version:
DECLARE #NumWeeks int
SET #NumWeeks = 26
SELECT s1.ProdID, s1.WeekEnd, COUNT(*) AS ZeroCount
FROM Sales s1
INNER JOIN Sales s2
ON s2.ProdID = s1.ProdID
AND s2.WeekEnd >= s1.WeekEnd
AND s2.WeekEnd <= DATEADD(WEEK, #NumWeeks + 1, s1.WeekEnd)
WHERE s1.Sales > 0
GROUP BY s1.ProdID, s1.WeekEnd
HAVING COUNT(*) >= #NumWeeks
Now, this is making a critical assumption, namely that there are no duplicate entries (only 1 per product per week) and that new data is actually entered every week. With these assumptions taken into account, if we look at the 27 weeks after a non-zero sales week and find that there were 26 total weeks with zero sales, then we can deduce logically that they had to be 26 consecutive weeks.
Note that this will ignore products that had zero sales from the start; there has to be a non-zero week to anchor it. If you want to include products that had no sales since the beginning, then add the following line after `WHERE s1.Sales > 0':
OR s1.WeekEnd = (SELECT MIN(WeekEnd) FROM Sales WHERE ProdID = s1.ProdID)
This will slow the query down a lot but guarantees that the first week of "recorded" sales will always be taken into account.
SELECT DISTINCT
s1.ProdId
FROM (
SELECT
ProdId,
ROW_NUMBER() OVER (PARTITION BY ProdId ORDER BY WeekEnd) AS rownum,
WeekEnd
FROM Sales
WHERE Sales <> 0
) s1
INNER JOIN (
SELECT
ProdId,
ROW_NUMBER() OVER (PARTITION BY ProdId ORDER BY WeekEnd) AS rownum,
WeekEnd
FROM Sales
WHERE Sales <> 0
) s2
ON s1.ProdId = s2.ProdId
AND s1.rownum + 1 = s2.rownum
AND DateAdd(WEEK, 26, s1.WeekEnd) = s2.WeekEnd;