I am facing problem with following query:
SELECT sum(CASE WHEN status.new_reg_yn='n'
AND month(status.visit_date)-1 = 8
AND year(status.visit_date) = 2015 THEN 1 ELSE 0 END)
FROM customer_visit_status_tbl status,
customer_details_tbl cust
WHERE status.customer_id = cust.customer_id
AND cust.client_id=65
GROUP BY status.customer_id
The problem is that this query is returning results for customer with same id though I used group by. For example, in the month of September, if same customer visits 5 times it is returning count as 5 instead of 1 though I used group by.
It is really unclear what you want... Yes, distinct customers for a given time period, but then you are taking the month of the date visited -1 and looking for that equal to 8. Being that current month is 9 (September), Are you just looking for those based on activity the month prior to whatever the current is? So, for example, if Sept, 2015, you want totals for Aug, 2015. In Jan, 2016, you would want Dec, 2015? If that is the case, you can use the current date to subtract 1 month and get that as basis of the query. Then you can have your additional specific client (65 in this case).
My (subselect sqlvars) pre-creates variables applied for the query. It computes one month ago by subtracting 1 month from whatever the current date it. Then uses that as basis of the month representing whatever was the prior month, and similarly for that respective year.
Since this will in essence create a single row return, there is no Cartesian result and you can just run with your original other tables for final counts.
select
count( distinct s.customer_id ) as UniqueCustomers
from
( select #oneMonthAgo := DATE_ADD(CURRENT_DATE, INTERVAL -1 MONTH),
#finalMonth := MONTH( #oneMonthAgo ),
#finalYear := YEAR( #oneMonthAgo ) sqlvars,
customer_visit_status_tbl s
JOIN customer_details_tbl c
on s.customer_id = c.customer_id
AND c.client_id = 65
where
s.new_reg_yn='n'
Update Ans -
Select count(*)
from
( SELECT distinct status.customer_id
FROM customer_visit_status_tbl status
, customer_details_tbl cust
WHERE status.customer_id = cust.customer_id
AND cust.client_id = 65
and status.new_reg_yn = 'n'
AND month(status.visit_date)-1 = 8
AND year(status.visit_date) = 2015
) customer_visited
Related
I am trying to retrieve closed and opened stock prices from the first and last date per month.
For some reason, the output of the 'end_date_closed_stock_price' is NULL.
Do you know any idea why it is giving this result?
Also, could you tell me the appropriate queries to retrieve the last date of the month?
The followings are my queries and output.
Thanks in advance!
SELECT YEAR(date) AS years
, MONTH(date) AS months
, CASE WHEN date = MAX(date) THEN close END end_date_closed_stock_price
, CASE WHEN date = MIN(date) THEN open END first_date_opened_stock_price
FROM nasdaq_feb_25_1971_feb_25_2021
GROUP
BY 1,2
ORDER
BY 1 DESC;
---OUTPUT---
2020 5 NULL 9382.349609
2019 1 NULL 6947.459961
2019 2 NULL 7266.279785
2019 3 NULL 7582.290039
There is probably a more efficient solution, but this should work:
WITH dates as
(SELECT YEAR(date) as years
,MONTH(date) AS months
,MAX(date) as end_date
,MIN(date) as first_date
FROM nasdaq_feb_25_1971_feb_25_2021
GROUP BY 1, 2)
SELECT dates.years
, dates.months
, price1.close as end_date_closed_stock_price
, price2.open as first_date_opened_stock_price
FROM dates
JOIN nasdaq_feb_25_1971_feb_25_2021 price1
ON price1.date = dates.end_date
JOIN nasdaq_feb_25_1971_feb_25_2021 price2
ON price2.date = dates.first_date
ORDER
BY 1 DESC, 2 DESC;
I have a table called months - this contains all 12 months of the calendar, the IDs correspond to the month number.
I will be running a query to retrieve 2 or 3 sequential months from this table, e.g
April & May
June, July, August
December & January
However I want to ensure that whenever December are January and retrieved, that it retrieves them in that order, and not January - December. Here is what I have tried:
SELECT * FROM `months`
WHERE start_date BETWEEN <date1> AND <date2>
ORDER BY
FIELD(id, 12, 1)
This works for December & January, but now when I try to retrieve January & February it does those in the wrong order, i.e "February - January" - I'm guessing because we specified 1 in the ORDER BY as the last value.
Anybody know the correct way to achieve this? As I mentioned this should also work for 3 months, so for example "November, December, January" and "December, January, February" should all be retrieved in that order.
If you want December first, but the other months in order, then:
order by (id = 12) desc, id
MySQL treats booleans as numbers, with "1" for true and "0" for false. The desc puts the 12s first.
EDIT:
To handle the more general case, you can use window functions. Assuming the numbers are consecutive, then the issue is trickier. This will work for 2 and 3-month spans:
order by (case min(id) over () > 1 then id end),
(case when id > 6 1 else 2 end),
id
I'm reluctant to think about a more general solution based only on months. After all, you can just use:
order by start_date
Or, if you have an aggregation query:
order by min(start_date)
to solve the real problem.
This is not "mysql solution" properly :
with cte (id, month) AS (
select id, month from months
union all
select id, month from months
)
, cte1 (id, month, r) as (select id, month, row_number() over() as r from cte )
select * from cte1
where id in (12, 1)
and r >= 12 order by r limit 2 ;
DECLARE
#monthfrom int = 12,
#monthto int = 1;
with months as (select 1 m
union all
select m+1 from months where m<12)
select m
from months
where m in (#monthfrom,#monthto)
order by
case when #monthfrom>#monthto
then
m%12
else
m
end
result:
12
1
Basically in MySQL this can be done the same way:
set #from =12;
set #to =1;
with recursive months(m) as (
select 1 m
union all
select m+1 from months where m<12)
select *
from months
where m in (#from,#to)
order by case when #from>#to then m%12 else m end;
I'm trying to write a query that aggregates data from a table.
Essentially I have a long list of devices that have been inventoried and eventually installed over the last couple of years.
I want to find the average amount of time between when the device was received and when it was installed, and then have that data sorted by the month the device was installed. BUT in each month's row, I also want to include the data from the previous months.
So essentially what I want to see is: (sorry for terrible formatting)
MonthInstalled | TimeToInstall | Total#Devices
-----------------+---------------+----------------------------
Jan | 10 Days | 5
Feb(=Jan+Feb) | 15 Days | 18 (5 in Jan + 13 in Feb)
Mar(=Jan+Feb+Mar)| 13 Days | 25 (5 + 13 + 7)
...
The query I currently have written looks like this:
INSERT INTO DevicesInstall
SELECT ROUND(AVG(DATEDIFF(dvc.dt_install , dvc.dt_receive)), 1) AS 'Install',
COUNT(dvc.dvc_model) AS 'Total Devices',
MAX(dvc.dt_install) AS 'Date',
loc.loc_campus AS 'Campus'
FROM dvc_info dvc, location loc
WHERE dvc.dvc_loc_bin = loc.loc_bin
AND dvc.dt_install < '20160201'
;
Although this is functional, I have to iterate this for each month manually, so it is not scale-able. Is there a way to condense this at all?
We can return the dates using an inline view (derived table), and then join to the dvc_info table, so we can get the "cumulative" results.
To get the results for:
Jan
Jan+Feb
Jan+Feb+Mar
We need to return three copies of the rows for Jan, and two copies of the rows for Feb, and then collapse the those rows into an appropriate group.
The loc_campus is being included in the SELECT list... not clear why that is needed. If we want results "by campus", then we need to include that expression in the GROUP BY clause. Otherwise, the value returned for that non-aggregate is indeterminate... we will get a value for some row "in the group", but it could be any row.
Something like this:
SELECT d.dt AS `before_date`
, loc.loc_campus AS `Campus`
, ROUND(AVG(DATEDIFF(dvc.dt_install,dvc.dt_receive)),1) AS `Install`
, COUNT(dvc.dvc_model) AS `Total Devices`
, MAX(dvc.dt_install) AS `latest_dt_install`
FROM ( SELECT '2016-01-01' + INTERVAL 1 MONTH AS dt
UNION ALL SELECT '2016-01-01' + INTERVAL 2 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 3 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 4 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 5 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 6 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 7 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 8 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 9 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 10 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 11 MONTH
UNION ALL SELECT '2016-01-01' + INTERVAL 12 MONTH
) d
CROSS
JOIN location loc
LEFT
JOIN dvc_info dvc
ON dvc.dvc_loc_bin = loc.loc_bin
AND dvc.dt_install < d.dt
GROUP
BY d.dt
, loc.loc_campus
ORDER
BY d.dt
, loc.loc_campus
Note that the value returned for d.dt will be the "up until" date. We're going to get '2016-02-01' returned for the January results. If we want to return a value of January date, we can use an expression in the SELECT list...
SELECT DATE_FORMAT(d.dt + INTERVAL -1 MONTH,'%Y-%m') AS `month`
Lots of options on query alternatives.
But it looks like the "big hump" is that to get cumulative results, we need to return multiple copies of the dvc_info rows, so the rows can be collapsed into each "grouping".
I recommend working on just the SELECT first. And get that tested working, before monkeying around to turn it into an INSERT ... SELECT.
FOLLOWUP
We can use any query as an inline view (derived table d) that returns a set of dates we want.
e.g.
FROM ( SELECT DATE_FORMAT(m.install_dt,'%Y-%m-01') + INTERVAL 1 MONTH AS dt
FROM dvc_install m
WHERE m.install_dt >= '2016-01-01'
GROUP BY DATE_FORMAT(m.install_dt,'%Y-%m-01') + INTERVAL 1 MONTH
) d
Note that with this approach, if there are no install_dt in February, we won't get back a row for February. Using the static UNION ALL SELECT approach allows us to get back "zero" counts, i.e. to return rows for months where there isn't an install_dt in that month. (But that's the answer to a different question... how do I get back a "zero" count for February when there aren't any rows for Februrary?)
Alternatively, if we have a calendar table e.g. cal that contains a list of the dates we want, we could just reference the table in place of the inline view, or the inline view query could get rows from that.
FROM ( SELECT cal.dt
FROM cal cal
WHERE cal.dt >= '2016-01-01'
AND cal.dt <= NOW()
AND DATE_FORMAT(cal.dt,'%d') = '01'
) d
Good Day,
I have a table that contains 3 columns. Date, Store, Straight_Sales. Each day a new record is created for each store with their previous day's sales.
What I am trying to do is generate a result set that has both current month to date sales for each location as well as the past year same MTD sales.
I can accomplish this by using two totally separate queries and result sets however I am trying to include these in the same query for reporting purposes.
Here are my two current queries that work just fine:
Last Year Month to Date:
SELECT SUM(summ_sales_daily.straight_sales), store_master.name
FROM
store_master
INNER JOIN summ_sales_daily ON store_master.unit = summ_sales_daily.store
WHERE YEAR(date)=YEAR(DATE_SUB(NOW(), INTERVAL 1 YEAR)) AND MONTH(date)=MONTH(NOW())
GROUP BY summ_sales_daily.store ORDER BY summ_sales_daily.store
Current Year Month to Date:
SELECT SUM(summ_sales_daily.straight_sales), store_master.name
FROM
store_master
INNER JOIN summ_sales_daily ON store_master.unit = summ_sales_daily.store
WHERE YEAR(date)=YEAR(NOW()) AND MONTH(date)=MONTH(NOW())
GROUP BY summ_sales_daily.store ORDER BY summ_sales_daily.store
I'd like these to return the current and previous years MTD in the same result along with the store name (hence the join)
Any help would be awesome!
Using MariaDB
You can either use conditional aggregation and move the different conditions into a case expression within the sum function:
SELECT
store_master.name
, SUM(CASE WHEN YEAR(date)=YEAR(DATE_SUB(NOW(), INTERVAL 1 YEAR)) THEN summ_sales_daily.straight_sales ELSE 0 END) last_year_sales
, SUM(CASE WHEN YEAR(date)=YEAR(NOW()) THEN summ_sales_daily.straight_sales ELSE 0 END) current_year_sales
FROM store_master
INNER JOIN summ_sales_daily ON store_master.unit = summ_sales_daily.store
WHERE MONTH(date)=MONTH(NOW())
GROUP BY summ_sales_daily.store
ORDER BY summ_sales_daily.store;
Or you can calculate the two different values in a couple of derived tables that you join with:
SELECT
store_master.name,
last_year.sales as previous_mtd,
current_year.sales as current_mtd
FROM store_master
LEFT JOIN (
SELECT store, SUM(straight_sales) sales
FROM summ_sales_daily
WHERE YEAR(date)=YEAR(DATE_SUB(NOW(), INTERVAL 1 YEAR)) AND MONTH(date)=MONTH(NOW())
GROUP BY store
) last_year ON store_master.unit = last_year.store
LEFT JOIN (
SELECT store, SUM(summ_sales_daily.straight_sales) sales
FROM summ_sales_daily
WHERE YEAR(date)=YEAR(NOW()) AND MONTH(date)=MONTH(NOW())
GROUP BY store
) current_year ON store_master.unit = current_year.store ;
Sample SQL Fiddle
The first solution would probably perform better.
I have a large number of records with a transaction datetime field going back several years. I would like to do a comparative analysis between the same timespan this year and last. How can I group by week over a 3 month range?
I'm running into problems using the YEARWEEK and WEEK functions because of the day the year 2012 starts of versus the day 2011 starts on.
Given that I have records with datetimes everyday from Jan 1st to the current day, and records with the same datetimes from the prior year, how can I group by week so the output is sums with dates like: 01/01/2011, 01/08/2011, 01/15/2011, etc., and 01/01/2012, 01/08/2012, 01/15/2012, etc.?
My query so far is as follows:
SELECT
DATE_FORMAT(A.transaction_date, '%Y-%m-%d') as date,
ROUND(sum(A.quantity), 3) AS quantity,
ROUND(sum(A.total_amount), 3) AS amount,
A.product_code,
D.fuel_type_code,
D.fuel_type_name,
C.customer_code,
C.customer_name
FROM
cl_transactions AS A
INNER JOIN
card AS B ON A.card_number=B.card_number
INNER JOIN
customer AS C ON B.customer_code=C.customer_code
INNER JOIN
fuel_type AS D ON A.fuel_type=D.fuel_type_code
WHERE
((A.transaction_date >= DATE_FORMAT(NOW() - INTERVAL 3 MONTH, '%Y-%m-01')) OR (A.transaction_date - INTERVAL 1 YEAR >= DATE_FORMAT(NOW() - INTERVAL 15 MONTH, '%Y-%m-01') AND A.transaction_date <= NOW() - INTERVAL 1 YEAR))
GROUP BY
A.transaction_date, fuel_type_code;
I would essentially like something that achieves the following pseudo-query:
GROUP BY
STARTING FROM THE OLDEST DATE (A.transaction_date + INTERVAL 6 DAY)
I started with an inner query using sqlvariables to build out from/to ranges for this year and last year of each respective start of year/month/day (ex: 2012-01-01 and 2011-01-01 respectively). From that, I'm also pre-formatting the date for final output so you have ONE master date basis for display reflecting that of whatever the "this year" week would be.
From that, I do a join to the transaction table where the transaction date is BETWEEN the respective start of current week and start of next week. Since date/time stamps include hour minute, 2012-01-01 by itself is implied as 12:00:00am (midnight) of the day. and between will go UP TO 7 days later 12:00:00 am. And that date will become the start date of the following week.
So, by joining on the date being between EITHER last yr or this yr time period, its the same group qualification. So the field selection does a ROUND( SUM( IF() )) per respective last year or this year. if the incoming transaction date is LESS than the current year's week start, then it must be a record from prior year, otherwise its for the current year. So, respectively, add the value itself, or zero as it applies.
So now, you have the group by. The week that it qualified for was already prepared from the inner query via "ThisYearWeekOf" formatted column, regardless of the otherwise computed "YEARWEEK()" or "WEEK()". The date ranges took care of that qualification for us.
Finally, I added the fuel-type as a join and included that as the group by. You have to group by all non-aggregate columns for proper SQL, although MySQL lets you get by by just grabbing the first entry for the given group if it is NOT so specified in group by.
To close, I DID include the information for the customer as you didn't have it in the group by and did not appear to be applicable... it would just arbitrarily grab one. However, I've added it to the group by, so now your records will show at the per customer level, per product and fuel type, how much sales and quantity between this year and last.
SELECT
JustWeekRange.ThisYearWeekOf,
CTrans.product_code,
FT.fuel_type_code,
FT.fuel_type_name,
C.customer_code,
C.customer_name,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, CTrans.Quantity, 0 )), 3) as LastYrQty,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, CTrans.total_amount, 0 )), 3) as LastYrAmt,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, 0, CTrans.Quantity )), 3) as ThisYrQty,
ROUND( SUM( IF( CTrans.transaction_date < JustWeekRange.ThisYrWeekStart, 0, CTrans.total_amount )), 3) as ThisYrAmt,
FROM
( SELECT
DATE_FORMAT(#ThisYearDate, '%Y-%m-%d') as ThisYearWeekOf,
#LastYearDate as LastYrWeekStart,
#ThisYearDate as ThisYrWeekStart,
#LastYearDate := date_add( #LastYearDate, interval 7 day ) LastYrStartOfNextWeek,
#ThisYearDate := date_add( #ThisYearDate, interval 7 day ) ThisYrStartOfNextWeek
FROM
(select #ThisYearDate := '2012-01-01',
#LastYearDate := '2011-01-01' ) sqlvars,
cl_transactions justForLimit
HAVING
ThisYrWeekStart < '2012-04-01'
LIMIT 15 ) JustWeekRange
JOIN cl_transactions AS CTrans
ON CTrans.transaction_date BETWEEN
JustWeekRange.LastYrWeekStart AND JustWeekRange.LastYrStartOfNextWeek
OR CTrans.transaction_date BETWEEN
JustWeekRange.ThisYrWeekStart AND JustWeekRange.ThisYrStartOfNextWeek
JOIN fuel_type FT
ON CTrans.fuel_type = FT.fuel_type_code
JOIN card
ON CTrans.card_number = card.card_number
JOIN customer AS C
ON card.customer_code = C.customer_code
GROUP BY
JustWeekRange.ThisYearWeekOf,
CTrans.product_code,
FT.fuel_type_code,
FT.fuel_type_name,
C.customer_code,
C.customer_name