Filtering multiple aggregated columns - sql

So I have a database with sales, I had to group sales by day of week so that each day has it's own column for the selected months. I need to filter out the aggregated sum for a day where there were no sales (the result would still be NULL), I did add the s.Amount_sold>0 in the where clause but I'm not sure if that's the correct solution to the problem. I've been trying to think of a way other than just repeating all of the sums in the where / having clause but no luck so far, would really appreciate some help.
SELECT
t.CALENDAR_MONTH_NAME AS SALES_MONTH,
UPPER(LEFT(p.PROD_NAME,CHARINDEX('&',p.PROD_NAME)-1))+' '+ SUBSTRING(p.PROD_NAME,CHARINDEX('&',p.PROD_NAME),LEN(p.PROD_NAME))+' ('+CAST(p.PROD_ID AS VARCHAR)+')' AS PRODUCT_NAME,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=1) THEN s.AMOUNT_SOLD END) AS Monday,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=2) THEN s.AMOUNT_SOLD END) AS Tuesday,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=3) THEN s.AMOUNT_SOLD END) AS Wednesday,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=4) THEN s.AMOUNT_SOLD END) AS Thursday,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=5) THEN s.AMOUNT_SOLD END) AS Friday,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=6) THEN s.AMOUNT_SOLD END) AS Saturday,
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=7) THEN s.AMOUNT_SOLD END) AS Sunday
FROM sh.CUSTOMERS c
JOIN sh.SALES s ON c.CUST_ID=s.CUST_ID
JOIN sh.TIMES t ON s.TIME_ID=t.TIME_ID
JOIN sh.PRODUCTS p ON s.PROD_ID=p.PROD_ID
WHERE s.PROD_ID = 5
AND (t.CALENDAR_QUARTER_NUMBER=2 AND t.CALENDAR_YEAR=2000) AND s.AMOUNT_SOLD>0
GROUP BY p.PROD_NAME,t.CALENDAR_MONTH_NAME,t.CALENDAR_MONTH_NUMBER,p.PROD_ID
ORDER BY t.CALENDAR_MONTH_NUMBER

Could you use
SUM(CASE WHEN (t.DAY_NUMBER_IN_WEEK=1) THEN IFNULL(s.AMOUNT_SOLD, 0) END) AS Monday

Related

Count sequence selected columns

I have query below, I want sequence result like the value of 'feb' will sum by jan and feb, value of 'mar' will sum by jan, feb and mar,... . Is there any way to get the result like that?
select A.location as location
, count(Case When SUBSTRING(A.base_date,5,2)='01' Then A.customer_no else null end) as "jan"
, count(Case When SUBSTRING(A.base_date,5,2)='02' Then A.customer_no else null end) as "feb"
....
, count(Case When SUBSTRING(A.base_date,5,2)='12' Then A.customer_no else null end) as "dec"
from table_income A group by A.location;
SQL is a much more effective language when you think in rows rather than columns (normalisation).
For example, having one row per month is much simpler...
SELECT
location,
SUBSTRING(base_date,5,2) AS base_month,
SUM(COUNT(customer_no))
OVER (
PARTITION BY location
ORDER BY SUBSTRING(base_date,5,2)
)
AS count_cust
FROM
table_income
GROUP BY
location,
SUBSTRING(base_date,5,2)
Side notes:
If your base_date is a string, it shouldn't be, use data-types relevant to the data
If your base_date is a date or timestamp, you should really use date/timestamp functions, such as EXTRACT(month FROM base_date).
You probably should also account for different years...
SELECT
location,
DATE_TRUNC('month', base_date) AS base_month,
SUM(COUNT(customer_no))
OVER (
PARTITION BY location, DATE_TRUNC('year', base_date)
ORDER BY DATE_TRUNC('month', base_date)
)
AS count_cust
FROM
table_income
GROUP BY
location,
DATE_TRUNC('month', base_date)
Try this :
SELECT A.location as location
, count(Case When SUBSTRING(A.base_date,5,2) in ('01') Then A.customer_no else null end) as "jan"
, count(Case When SUBSTRING(A.base_date,5,2) in ('01','02') Then A.customer_no else null end) as "feb"
....
, count(Case When SUBSTRING(A.base_date,5,2) in ('01','02',...'12') Then A.customer_no else null end) as "dec"
from table_income A group by A.location;

SQL case operation

Im fairly new with sql, and been trying to solve a problem where you have a table information about orders. In this case, Im trying to use the case operation to get a monthly report on orders, so I should have a column which states the year,another one which states the month, and then I should have columns for days 1-20,21-22,23-24 and above 25. Im trying to use the case operation to get the amount of orders that happened on those days.
I tried the following query :
SELECT
DATEPART(YEAR,date) AS year,DATEPART(MONTH,date) AS month,
COUNT(CASE WHEN DATEPART(DAY,date) BETWEEN 1 AND 20 THEN order ELSE 0 END) AS D1_D20,
COUNT(CASE WHEN DATEPART(DAY,date) BETWEEN 21 AND 22 THEN order ELSE 0 END) AS D21_D22,
COUNT(CASE WHEN DATEPART(DAY,date) BETWEEN 23 AND 24 THEN order ELSE 0 END) AS D23_D24,
COUNT(CASE WHEN DATEPART(DAY,date) > 25 THEN order ELSE 0 END) AS D25_END
FROM ORDERS
GROUP BY DATEPART(YEAR,date),DATEPART(MONTH,date)
Obviously the problem with that query is that, now I just get the total number of orders for each of the days, I know I should count the orders, but dont know the syntax. Help would be greatly appreciated!
Use SUM():
SELECT
DATEPART(YEAR, date) AS year, DATEPART(MONTH, date) AS month,
SUM(CASE WHEN DATEPART(DAY,date) BETWEEN 1 AND 20 THEN 1 ELSE 0 END) AS D1_D20,
SUM(CASE WHEN DATEPART(DAY,date) BETWEEN 21 AND 22 THEN 1 ELSE 0 END) AS D21_D22,
SUM(CASE WHEN DATEPART(DAY,date) BETWEEN 23 AND 24 THEN 1 ELSE 0 END) AS D23_D24,
SUM(CASE WHEN DATEPART(DAY,date) > 25 THEN 1 ELSE 0 END) AS D25_END
FROM ORDERS
GROUP BY DATEPART(YEAR, date), DATEPART(MONTH, date);
I would recommend using the functions DAY(), YEAR(), and MONTH() because they are simpler to type.
By the way, you can use COUNT() if you remove the ELSE clause. Your particular problem is that COUNT(0) = COUNT(1) because COUNT() counts non-NULL values. I prefer SUM() because it is more intuitive in this respect.

Query to show all months and show values where there are data for the corresponding months

I have a query and it shows the months where there is corresponding data. However, I would like to show all of the months in the year and have the months where there are no data shown as zero.
There is my SQL Statement:
SELECT DATENAME(MONTH, hb_Disputes.OPENED) AS MonthValue,
COUNT(CASE WHEN REV_CLS = 2 THEN 1 END) AS SmallCommercialIndust,
COUNT(CASE WHEN REV_CLS <> 2 THEN 1 END) AS Residential
FROM hb_Disputes
WHERE (hb_Disputes.ASSGNTO = 'E099255') AND (YEAR(hb_Disputes.OPENED) = YEAR(GETDATE()))
GROUP BY hb_Disputes.OPENED
And this is my output:
I also have a table name MonthName that shows all of the months in a year and I know I may need to use this to accomplish what I'm trying to achieve but I'm not sure how to get there:
If you have data in the table for all months, but the where clause is filtering it out, then the simplest method is to extend the conditional aggregation:
SELECT DATENAME(MONTH, d.OPENED) AS MonthValue,
SUM(CASE WHEN d.ASSGNTO = 'E099255' AND d.REV_CLS = 2 THEN 1 ELSE 0 END) AS SmallCommercialIndust,
SUM(CASE WHEN d.ASSGNTO = 'E099255' AND d.REV_CLS <> 2 THEN 1 ELSE 0 END) AS Residential
FROM hb_Disputes d
WHERE YEAR(d.OPENED) = YEAR(GETDATE())
GROUP BY DATENAME(MONTH, d.OPENED)
ORDER BY MIN(d.OPENED);
Note: This does not fix the issue in all cases. It should just be a simple way to modify your query -- and will often work.

SQL Pivoting With rollup (or a row totals)

I have the following query with some g/l accounts and sum totals by month:
Select Month, [41100],[42000],[45100],[42200],[42300],[42400],[45200],
[45205]
from ( Select ACC_0 , (CONVERT(NUMERIC(12,2),(AMTLED_0))) as Amount , MONTH(ACCDAT_0) as Month
from x3v6.CICPROD.GACCENTRYD with(nolock)
where YEAR(ACCDAT_0) = YEAR(GETDATE())
) as s1
PIVOT (SUM(Amount) FOR ACC_0 IN ([41100],[42000],[45100],[42200],[42300],[42400],[45200],
[45205])) as pivot1
Which produces the following result set:
Month 41100 42000 45100 42200 42300 42400 45200 45205
1 3857806.91 19987.61 49876.84 49078.59 2173.63 NULL 375.00 68.52
2 4459775.79 5145.69 64442.41 58102.00 2684.40 NULL 230.00 NULL
3 4311142.03 8594.31 44220.72 33850.08 3116.75 141.00 1917.50 NULL
4 4413788.57 5613.67 58038.20 55359.25 4398.67 NULL 4796.38 132.00
5 4251083.15 4372.07 48488.03 53592.00 2869.86 127.00 110.00 128.00
6 4353075.16 9705.83 53925.37 64104.00 2304.65 2822.78 153.41 NULL
7 4549485.41 10054.92 61607.99 65136.00 1531.66 186.30 265.50 NULL
8 4239075.39 16917.10 43012.02 51591.25 1538.01 1690.91 350.00 NULL
9 4331439.41 39248.15 56368.41 74928.00 1858.82 694.32 160.00 66.00
10 3673909.02 12283.42 38928.66 28608.00 NULL NULL 120.00 NULL
I would like to get a TOTAL tally by each ROW in a column to the right. How would I accomplish this with the PIVOT syntax? I tried using a grouping sets, but it didn't work for me.
Version is SQL Server 2012.
Thanks
One simple way, would be to not use PIVOT, and instead, use the SUM aggregate with CASE expressions.
SELECT Month,
SUM(CASE WHEN ACC_0 = '41100' THEN Amount END) AS [41100],
SUM(CASE WHEN ACC_0 = '42000' THEN Amount END) AS [42000],
SUM(CASE WHEN ACC_0 = '45100' THEN Amount END) AS [45100],
SUM(CASE WHEN ACC_0 = '42200' THEN Amount END) AS [42200],
SUM(CASE WHEN ACC_0 = '42300' THEN Amount END) AS [42300],
SUM(CASE WHEN ACC_0 = '42400' THEN Amount END) AS [42400],
SUM(CASE WHEN ACC_0 = '45200' THEN Amount END) AS [45200],
SUM(CASE WHEN ACC_0 = '45205' THEN Amount END) AS [45205],
SUM(Amount) AS [Total]
FROM (SELECT ACC_0,
(CONVERT(NUMERIC(12,2),(AMTLED_0))) AS Amount,
MONTH(ACCDAT_0) AS Month
FROM x3v6.CICPROD.GACCENTRYD WITH (NOLOCK)
WHERE YEAR(ACCDAT_0) = YEAR(GETDATE())
-- only get the ACC_0 values you need so Total is correct
AND ACC_0 IN ('41100','42000','45100','42200','42300','42400','45200','45205')
) AS s1
GROUP BY Month

Using multiple sum case lines in query

This code does exactly what I need it to do for the desired months. Basically provides Numerator and Denominator.
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-01-01' and '2013-01-31' THEN 1 ELSE 0 END) AS Total_Jan13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-01-01' and '2013-01-31' and [ind_ra_cfvmc_00-30] = 'YES' THEN 1 ELSE 0 END) AS RA_Jan13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-02-01' and '2013-02-28' THEN 1 ELSE 0 END) AS Total_Feb13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-02-01' and '2013-02-28' and [ind_ra_cfvmc_00-30] = 'YES' THEN 1 ELSE 0 END) AS RA_Feb13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-03-01' and '2013-03-31' THEN 1 ELSE 0 END) AS Total_Mar13,
It can get pretty tedious when you have multiple months...is there a more efficient way of performing this calculation?
THanks!
You could group them by the year and month in a sub-select and then manipulate that:
SELECT DATEPART(year, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date) AS TotalYear,
DATEPART(month, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date) AS TotalMonth,
COUNT(*) AS Total
FROM [table]
GROUP BY DATEPART(year, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date),
DATEPART(month, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date)
and that would give you the totals by month, without having to specify the months beforehand.