Show non zero values on SQL server - sql

have a query on how to get all dates to show in my query on SQL server. I have tried to GENERATE_SERIES but was struggling on how to fit it in: code below on query.
WITH
cte_data AS (
SELECT
customer_id,
FORMAT(event_time,'yyyy-MM') as _month,
'eventtype1' AS source
FROM Pay.Money
UNION
SELECT
customer_id,
FORMAT(event_time,'yyyy-MM') as _month,
'eventtype2' AS source
FROM Pay.Send)
SELECT
_month,
COUNT(customer_id) AS active_customer
FROM(
SELECT
customer_id,
_month,
_no
FROM(
SELECT
customer_id,
_month,
count(customer_id) AS _no
FROM cte_data
GROUP BY
_month,
customer_id)l
WHERE _no>2)j
GROUP BY _month
My data that comes out shows what I want but I would like to show 0 values for months that have no data next to them
Currently data is
10-2021 5
12-2021 9
02-2022 2
What I would like
10-2021 5
11-2021 0
12-2021 9
01-2022 0
02-2022 2

Related

SQL - Calculate the difference in number of orders by month

I am working on the orders table provided by this site, it has its own editor where you can test your SQL statements.
The order table looks like this
order_id
customer_id
order_date
1
7000
2016/04/18
2
5000
2016/04/18
3
8000
2016/04/19
4
4000
2016/04/20
5
NULL
2016/05/01
I want to get the difference in the number of orders for subsequent months.
To elaborate, the number of orders each month would be like this
SQL Statement
SELECT
MONTH(order_date) AS Month,
COUNT(MONTH(order_date)) AS Total_Orders
FROM
orders
GROUP BY
MONTH(order_date)
Result:
Month
Total_Orders
4
4
5
1
Now my goal is to get the difference in subsequent months which would be
Month
Total_Orders
Total_Orders_Diff
4
4
4 - Null = Null
5
1
1 - 4 = -3
My strategy was to self-join following this answer
This was my attempt
SELECT
MONTH(a.order_date),
COUNT(MONTH(a.order_date)),
COUNT(MONTH(b.order_date)) - COUNT(MONTH(a.order_date)) AS prev,
MONTH(b.order_date)
FROM
orders a
LEFT JOIN
orders b ON MONTH(a.order_date) = MONTH(b.order_date) - 1
GROUP BY
MONTH(a.order_date)
However, the result was just zeros (as shown below) which suggests that I am just subtracting from the same value rather than from the previous month (or subtracting from a null value)
MONTH(a.order_date)
COUNT(MONTH(a.order_date))
prev
MONTH(b.order_date)
4
4
0
NULL
5
1
0
NULL
Do you have any suggestions as to what I am doing wrong?
You have to use LAG window function in your SELECT statement.
LAG provides access to a row at a given physical offset that comes
before the current row.
So, this is what you need:
SELECT
MONTH(order_date) as Month,
COUNT(MONTH(order_date)) as Total_Orders,
COUNT(MONTH(order_date)) - (LAG (COUNT(MONTH(order_date))) OVER (ORDER BY (SELECT NULL))) AS Total_Orders_Diff
FROM orders
GROUP BY MONTH(order_date);
Here in an example on the SQL Fiddle: http://sqlfiddle.com/#!18/5ed75/1
Solution without using LAG window function:
WITH InitCTE AS
(
SELECT MONTH(order_date) AS Month,
COUNT(MONTH(order_date)) AS Total_Orders
FROM orders
GROUP BY MONTH(order_date)
)
SELECT InitCTE.Month, InitCTE.Total_Orders, R.Total_Orders_Diff
FROM InitCTE
OUTER APPLY (SELECT TOP 1 InitCTE.Total_Orders - CompareCTE.Total_Orders AS Total_Orders_Diff
FROM InitCTE AS CompareCTE
WHERE CompareCTE.Month < InitCTE.Month) R;
Something like the following should give you what you want - disclaimer, untested!
select *, Total_Orders - lag(Total_orders,1) over(order by Month) as Total_Orders_Diff
from (
select Month(order_date) as Month, Count(*) as Total_Orders
From orders
Group by Month(order_date)
)o

SQL Server group by date

SELECT [DATE], [AMOUNT], SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
GROUP BY [DATE], [AMOUNT];
In the code above I selecting a user's date, amount and the "SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'" is the running total of their costs over a period of dates. When I run this code I get the following results:
DATE AMOUNT Running Total
2018-10-05 100 100
2018-10-06 1000 1100
2018-10-07 5000 6100
2018-10-08 2000 8100
2018-10-09 1000 9100
2018-10-10 5000 14100
2018-10-11 3000 25100
2018-10-11 8000 25100
This works nicely but my issue is the last two rows. I wanted them to be grouped by their date and have the total amount for both same days, so it should be:
Date Amount Running Total
2018-10-11 11000 25100
Does anyone have an idea of how this can achieved? My [DATE] is of type DATE.
UPDATE!!!!
I've seen some of your solutions and they are good but its important I display the AMOUNT and the Running Total amount as well, so the final result should be...
DATE AMOUNT Running Total
2018-10-05 100 100
2018-10-06 1000 1100
2018-10-07 5000 6100
2018-10-08 2000 8100
2018-10-09 1000 9100
2018-10-10 5000 14100
2018-10-11 11000 25100
Thank you everyone for the help so far!
Group up the amounts and then do your cumulative total
WITH CTE
AS
(
SELECT A.Dt,
SUM(A.Amount) AS Amount
FROM (
VALUES ('2018-10-05',100),
('2018-10-06',1000),
('2018-10-07',5000),
('2018-10-08',2000),
('2018-10-09',1000),
('2018-10-10',5000),
('2018-10-11',3000),
('2018-10-11',8000)
) AS A(Dt,Amount)
GROUP BY A.Dt
)
SELECT C.Dt,
C.Amount,
SUM(C.Amount) OVER (ORDER BY C.Dt) AS CumTotal
FROM CTE AS C;
Try like below
SELECT [DATE],sum( [AMOUNT]), SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
GROUP BY [DATE]
If you need groping sum then why you are using window function, only aggregation is enough :
SELECT [DATE], SUM([AMOUNT])
FROM PeopleActi
WHERE INSTANCE = 'Bank' AND DATE IS NOT NULL
GROUP BY [DATE];
Try this
;WITH CTe([DATE],AMOUNT)
AS
(
SELECT '2018-10-05', 100 UNION ALL
SELECT '2018-10-06', 1000 UNION ALL
SELECT '2018-10-07', 5000 UNION ALL
SELECT '2018-10-08', 2000 UNION ALL
SELECT '2018-10-09', 1000 UNION ALL
SELECT '2018-10-10', 5000 UNION ALL
SELECT '2018-10-11', 3000 UNION ALL
SELECT '2018-10-11', 8000
)
SELECT DISTINCT [DATE],SUM(AMOUNT)OVER(PARTITION BY [DATE] ORDER BY [DATE]) AMOUNT , SUM(AMOUNT)OVER( ORDER BY [DATE]) AS RuningTot FROM CTe
Script
SELECT DISTINCT [DATE],
SUM(AMOUNT)OVER(PARTITION BY [DATE] ORDER BY [DATE]) AS AMOUNT,
SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
I would use a CTE to first group by Date, and then do your running total ..
So something like
with myAmounts AS
(
SELECT [DATE], SUM([AMOUNT]) AS Amount
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
GROUP BY [DATE]
)
SELECT [DATE], [AMOUNT], SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM myAmounts
GROUP BY [DATE], [AMOUNT]
;
HTH,
B
ps; just saw that its the same answer as another .. democoding in action
Every field in a group by is going to cause it to potentially create new lines. If you SUM the amount field and remove it from your grouping, that should solve the issue. EDIT: I see the issue, I provided a fully stand alone example of the query below that you can adapt.
DECLARE #PeopleActi TABLE ([DATE] DATE,[AMOUNT] MONEY)
INSERT INTO #PeopleActi SELECT '2018-10-05',100
INSERT INTO #PeopleActi SELECT '2018-10-06',1000
INSERT INTO #PeopleActi SELECT '2018-10-07',5000
INSERT INTO #PeopleActi SELECT '2018-10-08',2000
INSERT INTO #PeopleActi SELECT '2018-10-09',1000
INSERT INTO #PeopleActi SELECT '2018-10-10',5000
INSERT INTO #PeopleActi SELECT '2018-10-11',3000
INSERT INTO #PeopleActi SELECT '2018-10-11',8000
SELECT *, SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM (
SELECT [DATE], SUM([AMOUNT]) AS AMOUNT
FROM #PeopleActi
WHERE DATE IS NOT NULL
GROUP BY [DATE]
) a
GROUP BY [DATE],Amount
Try Subselect:
SELECT p.[DATE], p.[AMOUNT], SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM
(
select [date], sum([amount]) as Amount from PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
group by [date]
) p
GROUP BY p.[DATE], p.[AMOUNT]

SQL Year over year growth percentage from data same query

How do I calculate the percentage difference from 2 different columns, calculated in that same query? Is it even possible?
This is what I have right now:
SELECT
Year(OrderDate) AS [Year],
Count(OrderID) AS TotalOrders,
Sum(Invoice.TotalPrice) AS TotalRevenue
FROM
Invoice
INNER JOIN Order
ON Invoice.InvoiceID = Order.InvoiceID
GROUP BY Year(OrderDate);
Which produces this table
Now I'd like to add one more column with the YoY growth, so even when 2016 comes around, the growth should be there..
EDIT:
I should clarify that I'd like to have for example next to
2015,5,246.28 -> 346,15942029% ((R2015-R2014) / 2014 * 100)
If you save your existing query as qryBase, you can use it as the data source for another query to get what you want:
SELECT
q1.Year,
q1.TotalOrders,
q1.TotalRevenue,
IIf
(
q0.TotalRevenue Is Null,
Null,
((q1.TotalRevenue - q0.TotalRevenue) / q0.TotalRevenue) * 100
) AS YoY_growth
FROM
qryBase AS q1
LEFT JOIN qryBase AS q0
ON q1.Year = (q0.Year + 1);
Access may complain it "can't represent the join expression q1.Year = (q0.Year + 1) in Design View", but you can still edit the query in SQL View and it will work.
What you are looking for is something like this?
Year Revenue Growth
2014 55
2015 246 4.47
2016 350 1.42
You could wrap the original query a twice to get the number from both years.
select orders.year, orders.orders, orders.revenue,
(select (orders.revenue/subOrders.revenue)
from
(
--originalQuery or table link
) subOrders
where subOrders.year = (orders.year-1)
) as lastYear
from
(
--originalQuery or table link
) orders
here's a cheap union'd table example.
select orders.year, orders.orders, orders.revenue,
(select (orders.revenue/subOrders.revenue)
from
(
select 2014 as year, 2 as orders, 55.20 as revenue
union select 2015 as year, 2 as orders, 246.28 as revenue
union select 2016 as year, 7 as orders, 350.47 as revenue
) subOrders
where subOrders.year = (orders.year-1)
) as lastYear
from
(
select 2014 as year, 2 as orders, 55.20 as revenue
union select 2015 as year, 2 as orders, 246.28 as revenue
union select 2016 as year, 7 as orders, 350.47 as revenue
) orders

Grouping multiple selects within a SQL query

I have a table Supplier with two columns, TotalStock and Date. I'm trying to write a single query that will give me stock totals by week / month / year for a list of suppliers.
So results will look like this..
SUPPLIER WEEK MONTH YEAR
SupplierA 50 100 2000
SupplierB 60 150 2500
SupplierC 15 25 200
So far I've been playing around with multiple selects but I can't get any further than this:
SELECT Supplier,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) AS StockThisWeek,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) AS StockThisMonth,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-1-1'
GROUP BY Supplier
) AS StockThisYear
This query throws an error as each individual grouping returns multiple results. I feel that I'm close to the solution but can't work out where to go
You don't have to use subqueries to achieve what you want :
SELECT Supplier
, SUM(CASE WHEN Date >= CAST('2014-05-12' as DATE) THEN TotalStock END) AS StockThisWeek
, SUM(CASE WHEN Date >= CAST('2014-05-01' as DATE) THEN TotalStock END) AS StockThisMonth
, SUM(CASE WHEN Date >= CAST('2014-01-01' as DATE) THEN TotalStock END) AS StockThisYear
FROM StockBreakdown
GROUP BY Supplier
You may need to make the selects for the columns return only a single result. You could try this (not tested currently):
SELECT Supplier,
(
SELECT TOP 1 StockThisWeek FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisWeek
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) tmp1
WHERE tmp1.Supplier = Supplier
) AS StockThisWeek,
(
SELECT TOP 1 StockThisMonth FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisMonth
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) tmp2
WHERE tmp2.Supplier = Supplier
) AS StockThisMonth,
...
This selects the supplier and then tries to create two columns StockThisWeek and StockThisMonth by selecting the first entry from the select you created before. As through the GROUP BY there should only be one entry per supplier, so you don't lose and data.

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