How to get the right order? - sql

I have written queries to get the total qty of each month in 2016. Then I added another row called Total to sum up the total qty in 2016. But the ordermonth in the result turned out to be in a mess.
So, the question is is there any way to put the ordermonth in the right order? Both ASC and DESC are OK. Thanks in advance.
The query I've written:
WITH CTE AS(
SELECT CONVERT(CHAR(10), MONTH(orderdate)) AS ordermonth, SUM(qty) AS qty
FROM dbo.orders
WHERE YEAR(orderdate) = 2016
GROUP BY MONTH(orderdate)
)
SELECT * FROM CTE
UNION
SELECT 'Total', SUM(qty)
FROM dbo.orders
WHERE YEAR(orderdate) = 2016;
Current result:
ordermonth qty
1 4134
10 6454
11 9780
12 4000
2 5548
3 6970
4 3543
5 3309
6 4251
7 4997
8 6134
9 6926
Total 66046

First, you can do what you want without UNION. Something like this:
select coalesce(cast(month(orderdate) as varchar(255)), 'Total') as mon . . .
from . . .
group by grouping sets (month(orderdate), ())
order by month(orderdate)
No CTE, that's the entire query.

You can put into subquery and try like below:
select * from (
--your query including cte and union
) a
order by case when a.[Month] = 'Total' then 9999 else convert(int,a.[Month]) end

You can sort it by yyyymm, that is the best way even when you'll have more than one year, i.e.
order by orderyear * 100 + ordermonth
Or you can just add '0' for getting the right order like this:
order by right('0' + cast(ordermonth as varchar(2)), 2)

I found an easy way to solve this issue, that is, replacing UNION with UNION ALL.
WITH CTE AS(
SELECT CONVERT(CHAR(10), MONTH(orderdate)) AS ordermonth, SUM(qty) AS qty
FROM dbo.orders
WHERE YEAR(orderdate) = 2016
GROUP BY MONTH(orderdate)
)
SELECT * FROM CTE
UNION ALL
SELECT 'Total', SUM(qty)
FROM dbo.orders
WHERE YEAR(orderdate) = 2016;

Related

T-SQL. GroupBy Truncated DATETIME and order by

I have a simple table of Orders like:
Order
=========
Date (DATETIME)
Price (INT)
And I would like to know how much money we earned per every month and output should look like:
January-2018 : 100
February-2018: 200
...
January-2019: 300
...
I have the following SQL:
SELECT
DATENAME(MONTH , DATEADD(M, t.MDate , -1 ) ) + '-' + CAST(t.YDate as NVARCHAR(20)),
SUM(Price)
FROM
(
SELECT
DATEPART(YYYY, o.Date) as YDate,
DATEPART(MM, o.Date) as MDate,
Price
FROM [Order] o
) t
GROUP BY t.YDate, t.MDate
ORDER BY t.YDate, t.MDate
Is it ok or may be there is a better approach ?
format() is the way to go. I would recommend:
select format(o.date, 'MMMM-yyyy') as Monthyear,
sum(o.price) as total
from orders o
group by format(datenew, 'MMMM-yyyy')
order by min(o.date)
If you are using Sql server 2012 or + , you can make use of format function as well.
select cast('2018-02-23 15:34:09.390' as datetime) as datenew, 100 as price into #temp union all
select cast('2018-02-24 15:34:09.390' as datetime) as datenew, 300 as price union all
select cast('2018-03-10 15:34:09.390' as datetime) as datenew, 500 as price union all
select cast('2018-03-11 15:34:09.390' as datetime) as datenew, 700 as price union all
select cast('2019-02-23 15:34:09.390' as datetime) as datenew, 900 as price union all
select cast('2019-02-23 15:34:09.390' as datetime) as datenew, 1500 as price
select Monthyear, total from (
select format(datenew, 'MMMM-yyyy') Monthyear, format(datenew, 'yyyyMM') NumMMYY ,sum(price) total from #temp
group by format(datenew, 'MMMM-yyyy') , format(datenew, 'yyyyMM') ) test
order by NumMMYY
Since format will convert it to nvarchar, I had to create one more column in the subquery to order it properly. If you don't mind one more column you don't have to use subquery, and you can order it with another column. I could not find any other way (except for case statement which is not a very good idea) to do it in the same select
Output:
Monthyear Total
February-2018 400
March-2018 1200
February-2019 2400
Maybe just with this?
with Orders as (
select convert(date,'2018-01-15') as [date], 10 as Price union all
select convert(date,'2018-01-20'), 20 union all
select convert(date,'2018-01-30'), 30 union all
select convert(date,'2018-02-15'), 20 union all
select convert(date,'2018-03-15'), 40 union all
select convert(date,'2018-03-20'), 50
)
select
datename(month,o.Date) + '-' + datename(year,o.Date) + ': ' +
convert(varchar(max),SUM(Price))
FROM [Orders] o
group by datename(month,o.Date) + '-' + datename(year,o.Date)
order by 1 desc
I don't get well why are you removing one month in your query, I am not doing it. You can test it here: https://rextester.com/GRKK80105

multiple dates against count

I have 3 columns
Order_ID
Activation_Date
Order_Received_Date
So, if i distinctly count all the order_IDs on Order_Received_Date, I will get "Number of Orders" Similarly, if i distinctly count all the order_IDs on Activation_Date, I will get "Number of Activations"
What i want is two columns called "# Orders" and "# Activations"
Appreciate any inputs, thanks
I use union all for this type of calculation. The following is standard SQL so it should work in any database:
select thedate, sum(r) as numorders, sum(a) as numactivations
from (select activation_date as thedate, 1 as a, 0 as r from table t
union all
select order_received_date, 0, 1 from table t
) t
group by thedate
order by thedate;
If I understand you correctly, I guess the below query might help you solve this issue:
SELECT
OrderID,
ActivationDate,
OrderReceivedDate,
COUNT(OrderID) OVER (),
COUNT(ActivationDate) OVER ()
FROM
(
SELECT 1 AS OrderID, '2014-01-01' AS ActivationDate, '2013-12-15' AS OrderReceivedDate UNION
SELECT 2 AS OrderID, NULL AS ActivationDate, '2013-12-19' AS OrderReceivedDate UNION
SELECT 3 AS OrderID, '2014-03-01' AS ActivationDate, '2013-12-17' AS OrderReceivedDate UNION
SELECT 4 AS OrderID, '2014-04-01' AS ActivationDate, '2013-12-03' AS OrderReceivedDate
) t
I wish this helps you...

How to return the most ordered item for each month

I am trying to return the most ordered product per month, of the year 2007. I would like to see the name of the product, how many of them where ordered that month, and the month. I am using the AdventureWorks2012 database. I have tried a few different ways but each time multiple product orders are returned for the same month, instead of the one product that had the most order quantity that month. Sorry if this is not clear. I am trying to test myself so I make up my own questions and try to answer them. If anyone knows a site that have questions and answers like this so I can verify that would be super helpful! Thanks for any help. Here is the farthest I have been able to get with the query.
WITH Ord2007Sum
AS (SELECT sum(od.orderqty) AS sorder,
od.productid,
oh.orderdate,
od.SalesOrderID
FROM Sales.SalesOrderDetail AS od
INNER JOIN
sales.SalesOrderHeader AS oh
ON od.SalesOrderID = oh.SalesOrderID
WHERE year(oh.OrderDate) = 2007
GROUP BY ProductID, oh.OrderDate, od.SalesOrderID)
SELECT max(sorder),
s.productid,
month(h.orderdate) AS morder --, s.salesorderid
FROM Ord2007Sum AS s
INNER JOIN
sales.SalesOrderheader AS h
ON s.OrderDate = h.OrderDate
GROUP BY s.ProductID, month(h.orderdate)
ORDER BY morder;
Make a CTE that groups our products by month and creates a sum
;WITH OrderRows AS
(
SELECT
od.ProductId,
MONTH(oh.OrderDate) SalesMonth,
SUM(od.orderqty) OVER (PARTITION BY od.ProductId, MONTH(oh.OrderDate) ORDER BY oh.OrderDate) ProdMonthSum
FROM SalesOrderDetail AS od
INNER JOIN SalesOrderHeader AS oh
ON od.SalesOrderID = oh.SalesOrderID
WHERE year(oh.OrderDate) = 2007
),
Make a simple numbers table to break out each month of the year
Months AS
(
SELECT 1 AS MonthNum UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8
UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12
)
We query our months table against the data and select the top product for each month based on the sum
SELECT
m.MonthNum,
d.ProductID,
d.ProdMonthSum
FROM Months m
OUTER APPLY
(
SELECT TOP 1 r.ProductID, r.ProdMonthSum
FROM OrderRows r
WHERE r.SalesMonth = m.MonthNum
ORDER BY ProdMonthSum DESC
) d
Your group by statement should not include oh.OrderDate, od.SalesOrderID because this will aggregate your data to the incorrect level. You want the ProductID that was most commonly sold per month so the group by conditions become ProductID, datepart(mm,oh.OrderDate). As Andrew suggested the Row_Number function is useful in this case as it lets you create a key that is ordered by month and sorder and which resets each month. Finally in the outer query limits the results to the first instance (which is the highest quantity)for each month.
WITH Ord2007Sum
AS(
SELECT sum(od.orderqty) AS sorder,
od.productid,
datepart(mm,oh.OrderDate) AS 'Month'
row_number() over (partition by datepart(mm,oh.OrderDate)
Order by datepart(mm,oh.OrderDate)desc, sorder desc) row
FROM Sales.SalesOrderDetail AS od
INNER JOIN
sales.SalesOrderHeader AS oh
ON od.SalesOrderID = oh.SalesOrderID
WHERE datepart(yyyy,oh.OrderDate) = 2007
GROUP BY ProductID, datepart(mm,oh.OrderDate)
)
SELECT productid,
sorder,
[month]
FROM Ord2007Sum
WHERE row =1

How to get the real totals (without the TOP) when I do a ms access report with the SELECT TOP 10 ....?

When I do a report I use the variable
=sum([sell])
and the result here is the sum of TOP 10. My question is how do I show the result of the sum with all the elements, like the TOP 10 wouldn't exists?
SQL example:
Select top 10 name, cust, sell from sales
In practice the query is monstrous, big and dirty:
SELECT top 125 COD_FAM, NOME_FAM, ID_VENDEDOR, NOME_VENDEDOR, ID_ZONA, CONTA_CLI, SUB_CONTA_CLI, NOME_CLI, SUM(VENDA1) AS VENDAS1, SUM(VENDA2) AS VENDAS2, ROUND(IIF(SUM(VENDA1)=0, 9999, ((SUM(VENDA2)-SUM(VENDA1)))/abs(SUM(VENDA1))*100), 2) AS PER_DIFF FROM( SELECT quarter, month, COD_FAM, NOME_FAM, ID_VENDEDOR, NOME_VENDEDOR, ID_ZONA, CONTA_CLI, SUB_CONTA_CLI, NOME_CLI, VENDA AS VENDA1, 0 AS VENDA2 FROM STKQRY_VENDAS07_FAM_MONTH_VND_CLI_F1 WHERE year = '2012' AND Month between '00' and '05' UNION ALL SELECT quarter, month, COD_FAM, NOME_FAM, ID_VENDEDOR, NOME_VENDEDOR, ID_ZONA, CONTA_CLI, SUB_CONTA_CLI, NOME_CLI, 0 AS VENDA1, VENDA AS VENDA2 FROM STKQRY_VENDAS07_FAM_MONTH_VND_CLI_F1 WHERE year = '2013' AND Month between '00' and '05' ) GROUP BY COD_FAM, NOME_FAM, ID_VENDEDOR, NOME_VENDEDOR, ID_ZONA, CONTA_CLI, SUB_CONTA_CLI, NOME_CLI HAVING (SUM(VENDA1) > 1000 OR SUM(VENDA2) > 1000) ORDER BY vendas2 desc
You need to join 2 queries like
SELECT TOP 10 Company, SUM(Sales) from MyTable Group By Company --Query to get data for TOP 10
Union All
SELECT 'Grand Total', SUM(Sales) from MyTable --Query to get the Grand total

Oracle : sum this and this and this in a single query

I have on order table containing orders for last week and the ID of the driver who delivered them. It looks a little like this:
ORDERDATE, ORDERNO, DRIVER
23/01/2013, 901398503, 1
23/01/2013, 901332159, 1
23/01/2013, 901334158, 2
24/01/2013, 901338455, 1
25/01/2013, 902907513, 1
25/01/2013, 902338553, 2
25/01/2013, 903936533, 2
27/01/2013, 903944523, 1
27/01/2013, 903981522, 2
27/01/2013, 911334951, 1
28/01/2013, 911338851, 1
28/01/2013, 911339259, 1
28/01/2013, 912332555, 2
28/01/2013, 912336650, 2
29/01/2013, 912337655, 1
29/01/2013, 913969582, 1
29/01/2013, 913973583, 1
29/01/2013, 913982552, 1
29/01/2013, 916379158, 1
I'd like to select ORDERDATE, ORDERCOUNT, DRIVER_1_COUNT, DRIVER_2_COUNT.
so, date | total orders | total orders for driver 1 | total orders for driver 2
Also, I need zeros if ORDERDATE, ORDERCOUNT, DRIVER_1_COUNT or DRIVER_2_COUNT are 0 (or null).
(In oracle) I can select dates for each day last week, and a zero order count (placeholder) for each day like this:
select
TRUNC(NEXT_DAY(sysdate,'SUNDAY')-7 +i) ORDERDATE,
0 as ORDERCOUNT
from
(select rownum i from all_objects where rownum < 8)
I should be able to use this output to make sure there are no days missing in the final results (no orders on 26th in this example)
ORDERDATE,ORDERCOUNT
23/01/2013,0
24/01/2013,0
25/01/2013,0
26/01/2013,0
27/01/2013,0
28/01/2013,0
29/01/2013,0
I need this output:
ORDERDATE,ORDERCOUNT,DRIVER_1_COUNT,DRIVER_2_COUNT
23/01/2013,3,2,1
24/01/2013,1,1,0
25/01/2013,3,1,2
26/01/2013,0,0,0
27/01/2013,3,2,1
28/01/2013,4,2,2
29/01/2013,5,5,0
I can get ORDERDATE & ORDERCOUNT(simple sum) and union with the other query to avoid missing days, but I can't work out how to sum for each driver too.
Thanks in advance for you help.
Ed
In Oracle 11g, you can do this:-
SELECT *
FROM orders
PIVOT (
COUNT( ORDERNO )
FOR DRIVER IN (1,2,3)
)
For further explanation see pivot and unpivot queries in 11g
Firstly you need to summarise and crosstab results:
SELECT ORDERDATE, SUM(ORDERCOUNT) ORDERCOUNT,
SUM(DECODE(DRIVER,1,ORDERCOUNT,0)) DRIVER_1_COUNT,
SUM(DECODE(DRIVER,2,ORDERCOUNT,0)) DRIVER_2_COUNT
FROM (
SELECT ORDERDATE, DRIVER, COUNT(*) ORDERCOUNT
FROM YourTable
GROUP BY ORDERDATE, DRIVER
) S
GROUP BY ORDERDATE
There might be smarter ways to do this in Oracle
Then you need to fill in the blanks using by outer joining this to your dates:
(note the query above is aliased as 'T' in this query:)
SELECT D.ORDERDATE,
NVL(T.ORDERCOUNT,0) ORDERCOUNT,
NVL(T.DRIVER_1_COUNT,0) DRIVER_1_COUNT,
NVL(T.DRIVER_1_COUNT,0) DRIVER_2_COUNT
FROM
(
SELECT ORDERDATE, SUM(ORDERCOUNT) ORDERCOUNT,
SUM(DECODE(DRIVER,1,ORDERCOUNT,0)) DRIVER_1_COUNT,
SUM(DECODE(DRIVER,2,ORDERCOUNT,0)) DRIVER_2_COUNT
FROM
(
SELECT ORDERDATE, DRIVER, COUNT(*) ORDERCOUNT
FROM YourTable
GROUP BY ORDERDATE, DRIVER
) S
GROUP BY ORDERDATE
) T
RIGHT OUTER JOIN
(
SELECT
TRUNC(NEXT_DAY(sysdate,'SUNDAY')-7 +i) ORDERDATE
FROM (select rownum i from all_objects where rownum < 8)
) D
ON D.ORDERDATE = T.ORDERDATE
You'll have to select from a subquery. Something like this should work.
select orderdate, ordercount, sum(driver1) driver1count, sum(driver2) driver2count
from (
select orderdate
, case when driver = 1 then 1 else 0 end driver1
, case when driver = 2 then 1 else 0 end driver2
, count(*) ordercount
from yourtable
where whatever
group by orderdate
, case when driver = 1 then 1 else 0 end driver1
, case when driver = 2 then 1 else 0 end driver2
) you_need_an_alias_here
group by orderdate, ordercount
order by orderdate