Hello I am trying to get records of certain months. For example for this query I would want the user to see the current month which is "May" and one month from the past which would be "April". I don't want my query to run any other month from that point on. I am stuck with this query and cant figure it out. Basically I need a function in my query to automatically know this month and the 1 month before to show the records. Thank you
DECLARE #Year int
set #Year = 2014
SELECT d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
, COUNT(CASE WHEN MONTH(c.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(c.Funded_date) = 2 THEN 1 ELSE NULL END) Feburary
, COUNT(CASE WHEN MONTH(c.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(c.Funded_date) = 4 THEN 1 ELSE NULL END) April
, COUNT(CASE WHEN MONTH(c.Funded_date) = 5 THEN 1 ELSE NULL END) May
, COUNT(CASE WHEN MONTH(c.Funded_date) = 6 THEN 1 ELSE NULL END) June
, COUNT(CASE WHEN MONTH(c.Funded_date) = 7 THEN 1 ELSE NULL END) July
, COUNT(CASE WHEN MONTH(c.Funded_date) = 8 THEN 1 ELSE NULL END) August
, COUNT(CASE WHEN MONTH(c.Funded_date) = 9 THEN 1 ELSE NULL END) September
, COUNT(CASE WHEN MONTH(c.Funded_date) = 10 THEN 1 ELSE NULL END) October
, COUNT(CASE WHEN MONTH(c.Funded_date) = 11 THEN 1 ELSE NULL END) November
, COUNT(CASE WHEN MONTH(c.Funded_date) = 12 THEN 1 ELSE NULL END) December
, COUNT(*) 'Year to Date'
FROM tdealer a
JOIN tContact b ON a.contact_id = b.contact_id
JOIN tContract c ON a.dealer_id = c.dealer_id
JOIN tCompany d ON c.company_id = d.company_id
where YEAR (c.Funded_date) = #Year
GROUP BY d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
The trick here is to do two things:
Create an expression which converts any arbitrary DATETIME into the first day of the month in which that date occurred.
Use it appropriately in WHERE and GROUP BY clauses.
The expression is
DATE_FORMAT(whatever, '%Y-%m-01')
This takes, for example '2014-05-28 12:22:30', and turns it into '2014-05-01'. It's really handy to convert one valid DATE to another valid DATE, because then you can use all the good date arithmetic built into MySQL.
So, for example, if you want the first of the month before the present month you use this:
DATE_FORMAT(NOW(), '%Y-%m-01') - INTERVAL 1 MONTH
Here's the outline of your query:
SELECT DATE_FORMAT(c.Funded_date, '%Y-%m-01') AS month_beginning,
d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone,
COUNT(1) AS itemcount
FROM tdealer a
JOIN tContact b ON a.contact_id = b.contact_id
JOIN tContract c ON a.dealer_id = c.dealer_id
JOIN tCompany d ON c.company_id = d.company_id
where c.Funded_date >= DATE_FORMAT(NOW(), '%Y-%m-01') - INTERVAL 1 MONTH
AND c.Funded_date < DATE_FORMAT(NOW(), '%Y-%m-01') + INTERVAL 1 MONTH
GROUP BY DATE_FORMAT(c.Funded_date, '%Y-%m-01'),
d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
ORDER BY d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone,
DATE_FORMAT(c.Funded_date, '%Y-%m-01')
See how you choose the two-month period? It starts with the first day of the month before the present month, and ends with the first day of next month, but without including that day.
How to do this sort of thing is written up at http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
Related
I have this table:
id
amount
method
date
01
10
A
2022-01-24 12:27:14.440
02
80
A
2022-01-24 12:27:14.440
01
20
D
2022-02-24 12:27:14.440
01
10
D
2022-02-24 12:27:14.440
02
20
D
2022-02-24 12:27:14.440
03
30
D
2022-02-24 12:27:14.440
and I want this:
method
amount_sum_jan
n_transaction_jan
n_customer_jan
amount_sum_feb
n_transaction_feb
n_customer_feb
A
10
2
2
0
0
0
D
0
0
0
80
4
3
This is a table with 7 column and rows equal to the number of methods.
AMOUNT: sum of amount in one month of one method
N_TRANSACTIONS: number of transaction in one month with one method
N_CUSTOMER: number of customers (id) who used that method in one month
Can I get it with just one query?
You want to aggregate your data by method and have separate columns for January data and February data. You get this with conditional aggregation (CASE expression inside the aggregate function),
select
method,
sum(case when month(date) = 1 then amount else 0 end) as amount_sum_jan,
count(case when month(date) = 1 then 1 end) as n_transaction_jan,
count(distinct case when month(date) = 1 then id end) as n_customer_jan,
sum(case when month(date) = 2 then amount else 0 end) as amount_sum_feb,
count(case when month(date) = 2 then 1 end) as n_transaction_feb,
count(distinct case when month(date) = 2 then id end) as n_customer_feb
from mytable
group by method
order by method;
It is called pivot, and would for fix dates look like this.
An aggregation of the method for the year and month, and you can COUNT or SUM your number
select
`method`,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then `amount` ELSE 0 END) amount_sum_jan,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then 1 ELSE 0 END) n_transaction_jan,
COUNT(DISTINCT CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then `d` ELSE 0 END) n_customer_jan,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then `amount` ELSe 0 END) amount_sum_feb,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then 1 ELSe 0 END) n_transaction_feb,
COUNT(DISTINCT CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then `d` ELSe 0 END) n_customer_feb
from tab1
GROUP BY `method`
http://www.sqlfiddle.com/#!9/31d8ef/10
much more interesting would be to make that dynamic
I have my query like below
SELECT dates,
COUNT(link_data_id) as TotalClicks,
sum(case when link_redirect_status = 1 then 1 else 0 end) AS GoodClicks,
sum(case when link_redirect_status != 1 then 1 else 0 end) AS BadClicks
FROM tbl_calendar
LEFT JOIN tbl_links_data ON dates = CAST(link_data_time AS DATE)
WHERE (`dates` BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY dates
It's giving me expected output like below
But I want add one more condition called link_order_id ='abcde', so I am trying like below
SELECT dates,
COUNT(link_data_id) as TotalClicks,
sum(case when link_redirect_status = 1 then 1 else 0 end) AS GoodClicks,
sum(case when link_redirect_status != 1 then 1 else 0 end) AS BadClicks
FROM tbl_calendar
LEFT JOIN tbl_links_data ON dates = CAST(link_data_time AS DATE)
WHERE link_order_id = 'abcde'
AND (`dates` BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY dates
But it's giving me only two rows like below
Why I am getting only two rows instead of 8 rows like first picture?
Move the criteria in the WHERE clause to the ON clause of the join:
SELECT
c.dates,
COUNT(d.link_data_id) AS TotalClicks,
SUM(CASE WHEN d.link_redirect_status = 1 THEN 1 ELSE 0 END) AS GoodClicks,
SUM(CASE WHEN d.link_redirect_status != 1 THEN 1 ELSE 0 END) AS BadClicks
FROM tbl_calendar c
LEFT JOIN tbl_links_data d
ON c.dates = CAST(d.link_data_time AS DATE) AND
d.link_order_id = 'abcde'
WHERE (c.dates BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY c.dates;
My table schema is as follow:
Indexes:
products.id PRIMARY KEY
products.description UNIQUE
expenses.id PRIMARY KEY
expenses.product_id FOREIGN KEY to product.id
My goal is to load
Cost of each product of current month (AS costs_november)
Cost of each product of last month (AS costs_october)
Change in costs of current month compared to last (current month costs - last month costs) (AS costs)
Percentage change of current month costs compared to last (last month costs * 100 / current month costs) (AS percent_diff)
I've managed to code SQL that does exactly that:
SELECT description, (SUM(cost) - IFNULL(
(
SELECT SUM(cost)
FROM expenses
WHERE month = 9 AND year = 2019 AND product_id = e.product_id
GROUP BY product_id
), 0)) AS costs,
SUM(cost) * 100 /
(
SELECT SUM(cost)
FROM expenses
WHERE month = 9 AND year = 2019 AND product_id = e.product_id
GROUP BY product_id
) AS percent_diff,
SUM(cost) AS costs_october,
IFNULL(
(
SELECT SUM(cost)
FROM expenses
WHERE month = 9 AND year = 2019 AND product_id = e.product_id
GROUP BY product_id
), 0) AS costs_september
FROM expenses e
JOIN products p ON (e.product_id = p.id)
WHERE month = 10 AND year = 2019
GROUP BY product_id
ORDER BY product_id;
But is copy-pasting the same subquery three times really the solution? In theory it requires to run four queries per product. Is there a more elegant way?
Appreciate for any help!
I would address this with conditional aggregation:
select
p.description,
sum(case when e.month = 11 then e.cost else 0 end) costs_november,
sum(case when e.month = 10 then e.cost else 0 end) costs_october,
sum(case when e.month = 11 then e.cost else -1 * e.cost end) costs,
sum(case when e.month = 10 then e.cost else 0 end)
* 100
/ nullif(
sum(case when e.month = 11 then e.cost else 0 end),
0
) percent_diff
from expenses e
inner join products p on p.id = e.product_id
where e.year = 2019 and e.month in (10, 11)
goup by e.product_id
You can avoid repeating the same conditional sums by using a subquery (your RDBMS would probably optimize it anyway, but this tends to make the query more readable):
select
description,
costs_november,
costs_october,
costs_november - costs_october costs,
costs_october * 100 / nullif(costs_november, 0) percent_diff
from (
select
p.description,
sum(case when e.month = 11 then e.cost else 0 end) costs_november,
sum(case when e.month = 10 then e.cost else 0 end) costs_october
from expenses e
inner join products p on p.id = e.product_id
where e.year = 2019 and e.month in (10, 11)
goup by e.product_id
) t
You can calculate for all months and all products at one time:
SELECT year, month,
SUM(costs) as curr_month_costs,
LAG(SUM(costs)) OVER (ORDER BY year, month) as prev_month_costs,
(SUM(costs) -
LAG(SUM(costs)) OVER (ORDER BY year, month)
) as diff,
LAG(SUM(costs)) OVER (ORDER BY year, month) * 100 / SUM(costs)
FROM expenses e JOIN
products p
ON e.product_id = p.id
GROUP BY product_id, year, month
ORDER BY year, month, product_id;
You can use a subquery if you want to select only the current month.
dears i have below query that's gets the count based on cases and it's working fine
select users.firstName,users.lastName,users.id,users.phoneNumber,
count(CASE
WHEN orders.`orderStatus` = 4 THEN 1 ELSE null END) As completed,
count(CASE
WHEN orders.`orderStatus` = 5 THEN 1 ELSE null END) as CustomerCancelled,
count(CASE
WHEN orders.`orderStatus` = 11 THEN 1 ELSE null END) as providerCancelled,
count(`orders`.`createdAt`) as total,
from users,providers,orders
where
`orders`.`providerId` = `providers`.`id`
and
users.id = `providers`.userId
and
`orders`.`createdAt` >= (CURDATE() - INTERVAL 7 DAY)
GROUP BY users.id;
what I need to add is to get the count for CustomerCancelled/total and show it with each record
I tried to divide it like below but not working
select users.firstName,users.lastName,users.id,users.phoneNumber,
count(CASE
WHEN orders.`orderStatus` = 4 THEN 1 ELSE null END) As completed,
count(CASE
WHEN orders.`orderStatus` = 5 THEN 1 ELSE null END) as CustomerCancelled,
count(CASE
WHEN orders.`orderStatus` = 11 THEN 1 ELSE null END) as providerCancelled,
count(`orders`.`createdAt`) as total,
CustomerCancelled/total //// this is what i tried to do
from users,providers,orders
where
`orders`.`providerId` = `providers`.`id`
and
users.id = `providers`.userId
and
`orders`.`createdAt` >= (CURDATE() - INTERVAL 7 DAY)
GROUP BY users.id;
You can't use aliases in the SELECT part of the query. You need to explicitly write out the expression:
select users.firstName,
users.lastName,
users.id,
users.phoneNumber,
COUNT(CASE WHEN orders.`orderStatus` = 4 THEN 1 END) aS completed,
COUNT(CASE WHEN orders.`orderStatus` = 5 THEN 1 END) AS CustomerCancelled,
COUNT(CASE WHEN orders.`orderStatus` = 11 THEN 1 END) AS providerCancelled,
COUNT(`orders`.`createdAt`) AS total,
COUNT(CASE WHEN orders.`orderStatus` = 5 THEN 1 END) / COUNT(`orders`.`createdAt`) AS ratio_cancelled
FROM users
JOIN providers
JOIN orders
WHERE `orders`.`providerId` = `providers`.`id`
AND users.id = `providers`.userId
AND `orders`.`createdAt` >= (CURDATE() - INTERVAL 7 DAY)
GROUP BY users.id;
Note that you don't need an ELSE null in your CASE expressions as that is the default. Also you should write explicit JOIN statements rather than use the deprecated comma style implicit JOIN.
I have the following query in oracle so dont know how to convert into SQL Server.
WITH start_date AS
(
SELECT TO_DATE ( '01-Jan-2010'
, 'DD-Mon-YYYY'
) AS start_date
FROM dual
)
SELECT m.task_name
, COUNT (CASE WHEN TO_CHAR (d.task_date, 'DD') = '01' THEN 1 END) AS Day_1
, COUNT (CASE WHEN TO_CHAR (d.task_date, 'DD') = '02' THEN 1 END) AS Day_2
, COUNT (CASE WHEN TO_CHAR (d.task_date, 'DD') = '03' THEN 1 END) AS Day_3
...
, COUNT (CASE WHEN TO_CHAR (d.task_date, 'DD') = '31' THEN 1 END) AS Day_31
FROM task_master m
JOIN task_detail d ON m.task_id = d.task_id
JOIN start_date s ON d.task_date >= s.start_date
AND d.task_date < ADD_MONTHS (s.start_date, 1)
GROUP BY m.task_name
;
Have a look at using
DATEPART to check the day of month (use d or dd)
and
CONVERT(DATETIME,'01-Jan-2010')
for the Date
and
DATEADD to add the date use DATEADD (datepart ,number,date )