I have a SQL query which retrieving count with month.
Ex :
January - 5
February - 2 so on.
What I need to know is, if there are no record for particular month, how to print that month and display its count as 0.
This is my query:
select
DATETNAME(MONTH, c.postTime) as Month, COUNT(c.regNo) as Count
from
Company c
where
DATENAME(YEAR, c.postTime) = 2015
group by
c.postTime
This query returns these results:
MONTH | Count
-----------------
July | 2
August | 1
The result I'm looking for should be:
MONTH | Count
-----------------
January | 0
February| 0
March | 0
April | 0
May | 0
June | 0
July | 2
August | 1
.
.
.
so on...
One way is to drive them from anchor query where all the months' list are written explicitly, then left join your table company:
SELECT m.Name, COUNT(COALESCE(c.regNo, 0)) as Count
FROM
(
VALUES ('Janurary'), ('February'), ('March'),
('April'), ('May'), ('June'),
('July'), ('August'), ('September'),
('October'), ('November'), ('December')
) AS m(Name)
LEFT JOIN Company c ON DATETNAME(MONTH, c.postTime) = m.Name
WHERE DATENAME(YEAR, c.postTime) = 2015
GROUP BY m.Name;
If the table company doesn't have any particular month, it will be shown with count 0.
You can also use a recursive CTE to generate numbers from 1 to 12 and cast these integers to month names:
;WITH CTE
AS
(
SELECT 1 AS n
UNION ALL
SELECT n + 1
FROM CTE
WHERE n < 12
), Months
AS
(
SELECT DATENAME(Month,
DATEADD(Month,
n-1,
CAST('2015-01-01' AS datetime))) AS Name
FROM CTE
)
SELECT m.Name, COUNT(COALESCE(c.regNo, 0)) as Count
FROM Months AS m
LEFT JOIN Company c ON DATETNAME(MONTH, c.postTime) = m.Name
WHERE DATENAME(YEAR, c.postTime) = 2015
GROUP BY m.Name;
SELECT m.Name, COUNT(COALESCE(c.regNo, 0)) as Count
FROM
(
VALUES ('Janurary'), ('February'), ('March'),
('April'), ('May'), ('June'),
('July'), ('August'), ('September'),
('October'), ('November'), ('December')
) AS m(Name)
LEFT JOIN Company c ON DATETNAME(MONTH, c.postTime) = m.Name
AND DATENAME(YEAR, c.postTime) = 2015
GROUP BY m.Name;
Use And instead of "Where" keyword. Then It's working as expected.
Related
In an interview I have been asked to return the SQL query that reports the IDs of customers with the total purchases strictly increasing yearly
Table is something like below:
And the output expected is 11,22 as 33 is not strictly increasing.
I did not able to solve it however, on googling I found the solution which is:
WITH year_cte AS (
SELECT customer_id,
YEAR(order_date) AS year,
SUM(price) AS total
FROM Orders
GROUP BY customer_id, year
ORDER BY NULL
)
SELECT a.customer_id
FROM year_cte a
LEFT JOIN year_cte b
ON b.customer_id = a.customer_id AND b.year = a.year + 1
GROUP BY a.customer_id
HAVING SUM(a.total >= IFNULL(b.total, 0)) = 1 // Did not get this SUM
ORDER BY NULL;
Now my problem is that I am able to understand the solution apart from 1 line which is:
HAVING SUM(a.total >= IFNULL(b.total, 0)) = 1 // Did not get this SUM
Can someone please help me to understand why there is a condition inside the sum() and why it is equating with 1?
The a table contains the total price for a customer_id for the year prior to table b.
You want to retrieve all customer_ids having total strictly increasing so you need to check that the number of rows satisfying the condition a.total >= IFNULL(b.total, 0) are 1.
This case happens when the customer has a strictly increasing streak of totals during the years, the = 1 accounts for the fact that the last line will have a value of b.total equal to NULL (and so a.total >= 0 will satisfy the condition).
Using customer_id = 11 as an example:
customer_id | year | total
--------------------------
11 | 2019 | 150
11 | 2020 | 200
11 | 2021 | 250
For a.year = 2019 and b.year = 2020 you will have 150 >= 200.
For a.year = 2020 and b.year = 2021 you will have 200 >= 250.
For a.year = 2021 and b.year = 2022 you will have 250 >= 0.
The SUM of this expression will give 1.
I have two tables namely "appointment" and "skills_data".
Structure of appointment table is:
id_ap || ap_meet_date || id_skill || ap_status.
And the value of ap_status are complete, confirm, cancel and missed.
And the skills_data table contains two columns namely:
id_skill || skill
I want to get the count of total number of appointments for each of these conditions
ap_status = ('complete' and 'confirm'),
ap_status = 'cancel' and
ap_status = 'missed'
GROUP BY id_skill and year and
order by year DESC
I tried this query which only gives me count of one condition but I want to get other two based on group by and order by clauses as mentioned.
If there is no record(for example: zero appointments missed in 2018 for a skill) matching for certain conditions, then it should display the output value 0 for zero count.
Could someone please suggest me with a query whether I should implement multiple select query or CASE clause to achieve my expected results. I have lot of records in appointment table and want a efficient way to query my records. Thank you!
SELECT a.id_skill, YEAR(a.ap_meet_date) As year, s.skill,COUNT(*) as count_comp_conf
FROM appointment a,skills_data s WHERE a.id_skill=s.id_skill and a.ap_status IN ('complete', 'confirm')
GROUP BY `id_skill`, `year`
ORDER BY `YEAR` DESC
Output from my query:
id_skill | year | skill | count_comp_conf
-----------------------------------------
1 2018 A 20
2 2018 B 15
1 2019 A 10
2 2019 B 12
3 2019 C 10
My expected output should be like this:
id_skill | year | skill | count_comp_conf | count_cancel | count_missed
------------------------------------------------------------------------
1 2018 A 20 5 1
2 2018 B 15 8 0
1 2019 A 10 4 1
2 2019 B 12 0 5
3 2019 C 10 2 2
You can use conditional aggregation using case when expression
SELECT a.id_skill, YEAR(a.ap_meet_date) As year, s.skill,
COUNT(case when a.ap_status IN ('complete', 'confirm') then 1 end) as count_comp_conf,
COUNT(case when a.ap_status = 'cancel' then 1 end) as count_cancel,
COUNT(case when a.ap_status = 'missed' then 1 end) as count_missed
FROM appointment a inner join skills_data s on a.id_skill=s.id_skill
GROUP BY `id_skill`, `year`
ORDER BY `YEAR` DESC
SELECT a.id_skill,
YEAR(a.ap_meet_date) As year,
s.skill,
SUM(IF(a.ap_status IN ('complete', 'confirm'),1,0)) AS count_comp_conf,
SUM(IF(a.ap_status='cancel',1,0)) AS count_cancel,
SUM(IF(a.ap_status='missed',1,0)) AS count_missed
FROM appointment a,skills_data s WHERE a.id_skill=s.id_skill
GROUP BY `id_skill`, `year`
ORDER BY `YEAR` DESC;
Please try to use if condition along with sum.
With below query you will get output.
select id_skill ,
year ,
skill ,
count_comp_conf ,
count_cancel ,
count_missed ( select id_skill, year, skill, if ap_status ='Completed' then count_comp_conf+1, elseif ap_status ='cancelled' then count_cancel +1 else count_missed+1
from appointment a join skills_data s on (a.id_skill = s.id_skill) group by id_skill, year) group by id_skill,year
order by year desc;
Suppose, its May 2017 and I want to get records of April 2017.
My query fetches the record of April 2017 with count but I want to include empty dates as well.
So, a desired output will be like:
----------------------
id | date | count
----------------------
1 | 01/04/17 | 0 // 0 because this dates does not have any tickets.
2 | 02/04/17 | 0 // 0 because this dates does not have any tickets.
3 | 03/04/17 | 0 // 0 because this dates does not have any tickets.
4 | 04/04/17 | 0 // 0 because this dates does not have any tickets.
5 | 05/04/17 | 0 // 0 because this dates does not have any tickets.
6 | 06/04/17 | 0 // 0 because this dates does not have any tickets.
7 | 07/04/17 | 0 // 0 because this dates does not have any tickets.
8 | 08/04/17 | 0 // 0 because this dates does not have any tickets.
till 30/04/2017
15/04/17 shows 2 count as it contains 2 records and my query does says that as well
but
how I can include empty dates as well starting from 01/04/17 - 30/04/2017
The query is:
Ticket.includes(:line_items).where('tickets.created_at > ? AND
tickets.created_at < ?', Date.today.last_month.beginning_of_month,
Date.today.beginning_of_month).where.not(line_items: {id: nil}).count
Output is:
SELECT COUNT(DISTINCT "tickets"."id") FROM "tickets" LEFT OUTER JOIN
"line_items" ON "line_items"."ticket_id" = "tickets"."id" WHERE
(tickets.created_at > '2017-04-01' AND tickets.created_at < '2017-05-01') AND
("line_items"."id" IS NOT NULL)
The desired output should show the record like below:
Similarly, if user selects for last 7 days, it should show last 7 days records as well.
Any workaround to cope it?
Lets say you stored the user input in a variable named "no_of_days". You could do something like
data = Ticket.includes(:line_items).where('tickets.created_at > ? AND tickets.created_at < ?', Time.now.beginning_of_day - (no_of_days).days , Time.now.beginning_of_day).where.not(line_items: {id: nil}) #retrieves all data in a single query
(0..no_of_days).map do |day_from_now|
todays_data = data.select{|item| item.created_at < (Time.now.beginning_of_day - (day_from_now).days) && item.created_at > (Time.now.beginning_of_day - (day_from_now - 1).days)}
return { "day": (Date.today - day_from_now.days).strftime("%A %d-%m"),
"ticket_count": todays_data.size,
"total_sales": todays_data.inject(0){|sum, item| sum + item.line_item.quantity * item.line_item.rate }
end
In SQL-Server you could achieve in following:
DECLARE #MinDate DATE = '20170401',
#MaxDate DATE = '20170430';
SELECT t.Id, d.[Date], COALESCE(t.[Count],0) AS [Count]
FROM (
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
) d
LEFT JOIN yourTbl AS t ON d.Date = t.Date
Answer for PostgreSQL, but not for RoR as I don't know it:
SELECT dat::date, count(DISTINCT tic."id")
FROM generate_series('2017-04-01'::date, '2017-04-30'::date, interval '1 day') AS dat
LEFT JOIN (SELECT "tickets"."id", "tickets"."created_at"
FROM "tickets"
JOIN "line_items"
ON "line_items"."ticket_id" = "tickets"."id") tic
ON tic."created_at" = dat::date
GROUP BY dat
ORDER BY dat
or
SELECT dat::date, count(DISTINCT "tickets"."id")
FROM generate_series('2017-04-01'::date, '2017-04-30'::date, interval '1 day') AS dat
LEFT JOIN "tickets" ON "tickets"."created_at" = dat::date
AND EXISTS(SELECT 1 FROM "line_items"
WHERE "line_items"."ticket_id" = "tickets"."id" LIMIT 1)
GROUP BY dat
ORDER BY dat
generate_series creates set of rows that goes from param1 to param2 with interval at param3.
I used the following query
select month(SubmittedDate), count(policyid) from tblpolicy p join tlkppolicystatus s on p.StatusID=s.StatusID where SubmittedDate between
'2017-01-01' and sysdate() and s.StatusID=1 group by month(SubmittedDate);
This returns the following output which is correct as month number 3 and 4 don't have any data.
Month Total
-----|-----
1 | 62
2 | 34
5 | 1
But I want the output to be like
Month Total
-----|-----
1 | 62
2 | 34
3 | 0
4 | 0
5 | 1
So that means if any month do have any data then also it will show with a value 0
Thanks
If you have data for all months, but none of the data has a status of 1, then the simplest method is probably to use conditional aggregation:
select month(SubmittedDate), sum(s.StatusID = 1)
from tblpolicy p join
tlkppolicystatus s
on p.StatusID=s.StatusID
where SubmittedDate between '2017-01-01' and sysdate()
group by month(SubmittedDate);
Of course, if those conditions don't hold, then the left join with a derived table is the best solution.
Try this
select coalesce(t1.month,t2.month) as month, coalesce(ct1.count,0) as count from
(
select month(SubmittedDate) as month, count(policyid) as count
from tblpolicy p join tlkppolicystatus s on p.StatusID=s.StatusID
where SubmittedDate between
'2017-01-01' and sysdate() and s.StatusID=1 group by month(SubmittedDate)
) as t1 right join
(
select 1 as month union all
select 2 as month union all
select 3 as month union all
select 4 as month union all
select 5 as month union all
select 6 as month union all
select 7 as month union all
select 8 as month union all
select 9 as month union all
select 10 as month union all
select 11 as month union all
select 12 as month
) as t2 on t1.month <= t2.month;
I need some help to solve an issue with my query. I want to join the output of two select statements:
1st
select extract(year from createdDate) as year,
count(extract(year from createdDate)) as count
from table
where to_user_id= 322
group by extract(year from createdDate);
and its output
Year Count
2014 18
2015 117
2016 9
and 2nd query
select count(extract(year from createdDate)) as count
from table
where userId=322
group by extract(year from createdDate);
and its output
Count
18
110
11
I want to add this two tables into one table.
I want that type of output,
Year Count Count
2014 18 18
2015 117 110
2016 9 11
Note that I use to_user_id in query 1 but userId in query 2.
I tried to solved out this thing but I got repeated values in the output.
Anyone know the solution?
Write them as subqueries and join them together.
SELECT a.year, a.count AS t_user_count, b.count AS user_count
FROM (select YEAR(create_date) AS year, COUNT(*) AS count
FROM table
WHERE to_user_id = 322
GROUP BY year) AS a
JOIN (SELECT YEAR(create_date) AS year, COUNT(*) AS count
FROM table
WHERE user_id = 322
GROUP BY year) AS b
ON a.year = b.year