Trying to get Month to date sales value for each date - sql

I have Shipdate and Totaldue column in my salesOrderheader table.
My current query is like this
select
[ShipDate], SUM([TotalDue]) Total
from
Sales.SalesOrderHeader
where
[ShipDate] between '2005-08-1' and '2005-08-31'
group by
[ShipDate]
This returns the result for total sales for each day, I want a result where each date column corresponds to a value which shows month to date sale (i.e. totaldue here). Like if total due for 2005-08-1 is $1000 ,total due for 2005-08-2 is $3000 and total due for 2005-08-3 is $6000 then result should be,
ShipDate MTD Value
-----------------------
2005-08-1 1000
2005-08-2 4000
2005-08-3 11000
And so on till 2008-08-31. I am kind of new to database and can't seem to figure this out. If anybody has idea how do to it, please help me out. Thanks

You could do it like this:
select soh1.ShipDate, SUM(soh2.TotalDue) from
(select distinct ShipDate from sales.SalesOrderHeader
where ShipDate < '2005-09-01' and ShipDate >= '2005-08-01') as soh1
join sales.SalesOrderHeader soh2
on soh2.ShipDate <= soh1.ShipDate and soh2.ShipDate >= '2005-08-01'
group by soh1.ShipDate order by soh1.ShipDate
The join will create the following resultset:
ShipDate MTD Value
2005-08-1 1000
2005-08-2 1000
2005-08-2 4000
2005-08-3 1000
2005-08-3 4000
2005-08-3 11000
and the Sum and Group By joins the dates togheter.

This works and performs reasonably well:
;with soh as (
select ShipDate, sum(TotalDue) as TotalDue
from Sales.SalesOrderHeader
where ShipDate between '20050801' and '20050831'
group by ShipDate
)
select ShipDate, sum(TotalDue) over(order by ShipDate) as Total
from soh

Related

SQL aggregate query repeating primary key column

I am querying two database tables, Invoice and Payments. I want to get the sum of all payments for a particular invoice, however the results 'repeat' the invoice numbers instead of aggregating the values.
The tables are
Invoices
invoiceID
dueDate
invoiceAmount
Payments
paymentID
invoiceID
fine
amount
This is my query :
SELECT
Invoices.invoiceID, invoiceAmount,
COALESCE((SUM (fine + amount)), 0) AS 'Total'
FROM
Payments, Invoices
WHERE
Payments.invoiceID = Invoices.invoiceID
GROUP BY
Invoices.invoiceID, dueDate, datePaid, invoiceAmount
ORDER BY
Invoices.invoiceID
The results of this are something like
invoiceID invoiceAmount Total
---------------------------------
2 270000 170000
2 270000 100000
67 400000 150000
67 400000 250000
I expect the output to be
invoiceID invoiceAmount Total
----------------------------------
2 270000 270000
67 400000 400000
use join and remove dueDate, datePaid from group by
SELECT
Invoices.invoiceID, invoiceAmount,
COALESCE((SUM (fine + amount)), 0) AS 'Total'
FROM
Payments join Invoices
on
Payments.invoiceID = Invoices.invoiceID
GROUP BY
Invoices.invoiceID,invoiceAmount
ORDER BY
Invoices.invoiceID
I suspect that you really want a left join to capture invoices that have no payments:
SELECT i.invoiceID, i.invoiceAmount,
SUM(COALESCE(fine, 0) + COALESCE(amount, 0)) as Total
FROM Invoices i LEFT JOIN
Payments p
ON p.invoiceID = i.invoiceID
GROUP BY i.invoiceID, i.invoiceAmount
ORDER BY i.invoiceID;
Note that COALESCE() is used on each column. If one of the values is NULL, then this probably does what you intend.

Summing a column over a date range in a CTE?

I'm trying to sum a certain column over a certain date range. The kicker is that I want this to be a CTE, because I'll have to use it multiple times as part of a larger query. Since it's a CTE, it has to have the date column as well as the sum and ID columns, meaning I have to group by date AND ID. That will cause my results to be grouped by ID and date, giving me not a single sum over the date range, but a bunch of sums, one for each day.
To make it simple, say we have:
create table orders (
id int primary key,
itemID int foreign key references items.id,
datePlaced datetime,
salesRep int foreign key references salesReps.id,
price int,
amountShipped int);
Now, we want to get the total money a given sales rep made during a fiscal year, broken down by item. That is, ignoring the fiscal year bit:
select itemName, sum(price) as totalSales, sum(totalShipped) as totalShipped
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
group by itemName
Simple enough. But when you add anything else, even the price, the query spits out way more rows than you wanted.
select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
group by itemName, price
Now, each group is (name, price) instead of just (name). This is kind of sudocode, but in my database, just this change causes my result set to jump from 13 to 32 rows. Add to that the date range, and you really have a problem:
select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
and orderDate between 150101 and 151231
group by itemName, price
This is identical to the last example. The trouble is making it a CTE:
with totals as (
select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped, orderDate as startDate, orderDate as endDate
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
and orderDate between startDate and endDate
group by itemName, price, startDate, endDate
)
select totals_2015.itemName as itemName_2015, totals_2015.price as price_2015, ...
totals_2016.itemName as itemName_2016, ...
from (
select * from totals
where startDate = 150101 and endDate = 151231
) totals_2015
join (
select *
from totals
where startDate = 160101 and endDate = 160412
) totals_2016
on totals_2015.itemName = totals_2016.itemName
Now the grouping in the CTE is way off, more than adding the price made it. I've thought about breaking the price query into its own subquery inside the CTE, but I can't escape needing to group by the dates in order to get the date range. Can anyone see a way around this? I hope I've made things clear enough. This is running against an IBM iSeries machine. Thank you!
Depending on what you are looking for, this might be a better approach:
select 'by sales rep' breakdown
, salesRep
, '' year
, sum(price * amountShipped) amount
from etc
group by salesRep
union
select 'by sales rep and year' breakdown
, salesRep
, convert(char(4),orderDate, 120) year
, sum(price * amountShipped) amount
from etc
group by salesRep, convert(char(4),orderDate, 120)
etc
When possible group by the id columns or foreign keys because the columns are indexed already you'll get faster results. This applies to any database.
with cte as (
select id,rep, sum(sales) sls, count(distinct itemid) did, count(*) cnt from sommewhere
where date between x and y
group by id,rep
) select * from cte order by rep
or more fancy
with cte as (
select id,rep, sum(sales) sls, count(distinct itemid) did, count(*) cnt from sommewhere
where date between x and y
group by id,rep
) select * from cte join reps on cte.rep = reps.rep order by sls desc
I eventually found a solution, and it doesn't need a CTE at all. I wanted the CTE to avoid code duplication, but this works almost as well. Here's a thread explaining summing conditionally that does exactly what I was looking for.

PRINT only SUM by year (group by Territory) in 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.

Join two Queries so that the second query becomes a row in the results of query 1

I have two queries that I would like to combine so i can make a chart out of the results.
The results have to be very specific or the chart will not display the information properly
I am using MS SQL in Crystal Reports 11
Below is the results I am looking for.
Date Invoice Type Amount
2012/08 Customer Payment 500
2012/08 Customer Invoice 1000
2012/08 Moving Balance 1500
2012/09 Customer Invoice 400
2012/09 Moving Balance 1900
2012/10 Interest 50
2012/10 Moving Balance 1950
So the First query returns the following results
Date Invoice Type Amount
2012/08 Customer Payment 500
2012/08 Customer Invoice 1000
2012/09 Customer Invoice 400
2012/10 Interest 50
and the second query returns
Date Invoice Type Amount
2012/08 Moving Balance 1500
2012/09 Moving Balance 1900
2012/10 Moving Balance 1950
The second query is very long and complicated with a join .
What is the best way of joining these two queries
so that I have one column called invoice Type ( as the chart is based on this field)
that covers all the invoice types plus the moving balance
I assume that the place of the Moving Balance rows inside the result set is important.
You can do something like this:
select date, invoice_type, amount
from
(
select date, invoice_type, amount from query1
union all
select date, invoice_type, amount from query2
)
order by date, case invoice_type when 'Moving Balance' then 1 else 0 end
This first appends the results of the second query to the results of the first query and then reorders the resulting list first by date and then by the invoice type in such a way that the row with Moving balance will come last.
With the actual queries you have given, it should look something like this:
select date, invoice_type, amount
from
(
SELECT
CONVERT(VARCHAR(7),case_createddate, 111) AS Date,
case_invoicetype as invoice_type,
Sum(case_totalexvat) as amount
FROM cases AS ca
WHERE case_primaryCompanyid = 2174 and
datediff(m,case_createddate,getDate())
union all
select
CONVERT(VARCHAR(7),ca.case_createddate, 111) AS Date,
'Moving Balance' as Invoice_Type,
sum(mb.Amount) as Amount
from
cases as ca
left join (
select
case_primaryCompanyId as ID,
case_createdDate,
case_TotalExVat as Amount
from
cases
) mb
on ca. case_primaryCompanyId = mb.ID
and ca.case_createdDate >= mb.case_CreatedDate
where
ca.case_primaryCompanyId = 2174 and
ca.case_createdDate > DATEADD(m, -12, current_timestamp)
group by
case_primaryCompanyId,
CONVERT(VARCHAR(7),ca.case_createddate, 111)
order by ca.case_primaryCompanyid, CONVERT(VARCHAR(7),ca.case_createddate, 111)
)
order by date, case invoice_type when 'Moving Balance' then 1 else 0 end
You can use Union and can use Order by clause
Select * from (Query 1
Union
Query 2
) as a Order by a.Date Asc

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