I have a table with ID and Date columns.
Table A:
ID
Date
24
2019-10-29
24
2019-10-30
24
2019-10-31
.....
....
I need to add a column called "Week_Num" such that:
If there is a new "ID" and the first date begins on a Tuesday (eg: 29th Oct,2019 in ID 24 is Tuesday) then the week will start with 1
Week will always end on Saturday irrespective of whether 7 days are over or not with only one exception (see point 3)
If there is a new "ID" and the first date begins before Tuesday (eg: 25th Oct,2020 in ID 25 is Sunday) then the week will start with 0 and once it reaches Tuesday the week_num will become 1.
Expected Output:
ID
Date
Day_Of_Week
Week_Num
24
2019-10-29
Tue
1
24
2019-10-30
Wed
1
24
2019-10-31
Thur
1
24
2019-11-01
Fri
1
24
2019-11-02
Sat
1
24
2019-11-03
Sun
2
24
2019-11-04
Mon
2
24
2019-11-05
Tues
2
24
2019-11-06
Wed
2
24
2019-11-07
Thurs
2
24
2019-11-08
Fri
2
24
2019-11-09
Sat
2
24
..........
.
..
24
2020-03-14
.
..
25
2020-10-25
Sun
0
25
2020-10-26
Mon
0
25
2020-10-27
Tue
1
25
2020-10-28
Wed
1
25
2020-10-29
Thur
1
25
2020-10-30
Fri
1
25
2020-10-31
Sat
1
What I have so far:
select
distinct ID,min(Date) over (partition by ID order by date) as firstTuesdayOfSeason
from
TableA
group by ID,Date
having datepart(weekday,Date)=3
which gets the first Tuesday for every new ID and gives the following output:
ID
firstTuesdayOfSeason
24
2019-10-29
25
2020-10-27
I was thinking of joining this table with the Table A (the one with ID, Date as columns) but I don't know how to implement the weird Saturday logic.
This might work for you.
Find min-date, offset to Tuesday for each ID, and calculate using date-diff for each row against the min-date
select a.ID, a.Date, weekday(a.Date), greatest(0, 1 + floor((datediff(a.Date, b.since) - b.off) / 7)) week_num
from TableA a
join (
select ID, min(Date) As since, (case when weekday(min(Date)) > 1 then 8 - weekday(min(Date)) else 1 - weekday(min(Date)) end) off
from TableA
group by ID
) b ON a.ID = b.ID
order by a.ID, a.Date
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=657c96465338a398ced73bc256ff92c0
Related
I am trying to find the last entry for the previous years quarter.
All I can access is year i.e 2021 and quarter i.e 1
Here is the data in my database:
id
name
start
end
16
April 2021
2021-04-01
2021-04-30
15
March 2021
2021-03-01
2021-03-31
14
February 2021
2021-02-01
2021-02-28
57
November 2020
2020-11-01
2020-11-30
55
October 2020
2020-10-01
2020-10-31
29
September 2020
2020-09-01
2020-09-30
27
July 2020
2020-07-01
2020-07-31
24
April 2020
2020-04-01
2020-04-30
23
March 2020
2020-03-01
2020-03-31
22
February 2020
2020-02-01
2020-02-29
21
January 2020
2020-01-01
2020-01-31
Using the MySQL quarter function I can get it to print out the quarter as an integer in another column:
SET #given_year = 2021;
SET #given_quarter = 1;
SELECT
id, name, start, end, QUARTER(end) as "Q"
FROM
submissions
id
name
start
end
Q
16
April 2021
2021-04-01
2021-04-30
2
15
March 2021
2021-03-01
2021-03-31
1
14
February 2021
2021-02-01
2021-02-28
1
57
November 2020
2020-11-01
2020-11-30
4
55
October 2020
2020-10-01
2020-10-31
4
29
September 2020
2020-09-01
2020-09-30
3
27
July 2020
2020-07-01
2020-07-31
3
24
April 2020
2020-04-01
2020-04-30
2
23
March 2020
2020-03-01
2020-03-31
1
22
February 2020
2020-02-01
2020-02-29
1
21
January 2020
2020-01-01
2020-01-31
1
I tried using WHERE and LIKE but it is returning 0 rows:
SELECT * FROM (
SELECT
id, name, start, end, QUARTER(end) as "Q"
FROM
submissions as s
) AS vs
WHERE
vs.end
LIKE
#given_year
AND
vs.Q < #given_quarter
I also need to account for the possibility that there may be no rows this year and I need to find the previous year.
For example with these two rows, if I was passed the year 2021 and quarter 1 I would need to return November of the previous year and a different quarter.
id
name
start
end
Q
14
February
2021
2021-02-01
2021-02-28
57
November
2020
2020-11-01
2020-11-30
If I understand correctly, you want all the rows from the quarter in the data before a given quarter. You can filter and use dense_rank():
select s.*
from (select s.*,
dense_rank() over (order by year(start) desc, quarter(start) desc) as seqnum
from submissions s
where year(start) < #given_year or
(year(start) = #given_year and quarter(start) < #given_quarter)
) s
where seqnum = 1;
The above returns all rows from the previous quarter (which is what I thought you wanted). If you want only one row:
select s.*
from submissions s
where year(start) < #given_year or
(year(start) = #given_year and quarter(start) < #given_quarter)
order by start desc
limit 1;
I have 2 tables
cumulative_money_in:
Cumulative_amount | date
500 | Apr 12
570 | Apr 14
620 | Apr 16
cumulative_money_out:
Cumulative_amount | date
100 | Apr 13
170 | Apr 15
200 | Apr 17
I want my result in this example to be:
Remaining_balance | date
500 | Apr 12
400 | Apr 13
470 | Apr 14
400 | Apr 15
450 | Apr 16
420 | Apr 17
i.e. I want to have a timeline of how the remaining balance (money_in-money_out) changes
What I have so far is this:
SELECT date, (COALESCE(money_in.cumulative_amount,0) - COALESCE(money_out.cumulative_amount,0))
FROM money_in
FULL OUTER JOIN money_out
ON money_in.date <= money_out.date
Which clearly is not what I want to have
You can union all your tables, order by date. and then calculate remaining money for each interval with a left join:
with money_start as (
select *, 1 flag from money_in
union all
select *, -1 flag from money_out
),
money as (
select * from money_start order by date
)
select case when m1.flag = 1
then m1.amount - coalesce(m2.amount, 0)
else m2.amount - m1.amount end remaining,
m1.date
from money m1
left join money m2 on m2.date = date_sub(m1.date, interval 1 day)
order by m1.date;
Output:
remaining
date
500
2021-04-12
400
2021-04-13
470
2021-04-14
400
2021-04-15
450
2021-04-16
420
2021-04-17
I have a set of data that looks like this
Cust Id order date Ordered Product
1 Jan 2 1
1 Jan 5 2
1 March 14 1
1 September 9 1
1 December 12 2
2 Jan 5 1
2 Feb 13 2
3 March 12 2
3 April 5 3
3 June 10 2
and my output should look like this
Cust Id order Date Order product
1 Jan 31 3
1 feb 29 0
1 Mar31 1
1 Apr 30 0
1 May 31 0
1 June 30 0
1 July 31 0
1 Aug 31 0
1 Sept 30 1
1 oct 31 0
1 Nov 30 0
1 Dec 31 2
and I have got this far
1 January 31 3
1 March 31 1
1 September 30 1
1 December 31 2
and my code is
select customer_id,
date_format(last_day(order_date), '%M %d') as new_months,
sum(products_ordered) as total
from amazon_test
where customer_id =1
group by new_months, customer_id;
I currently stuck at the part where I need to have all the months and '0' as the output since no orders were made.
If you are running MySQL 8.0, one option is to use a recursive query to generate the months, and then bring the table with a left join:
with recursive months as (
select customer_id, date_format(min(order_date), '%Y-%m-01') order_date, max(order_date) max_order_date
from amazon_test
group by customer_id
union all
select customer_id, order_date + interval 1 month, max_order_date
from months
where order_date + interval 1 month < max_order_date
)
select
m.customer_id,
date_format(last_day(m.order_date), '%M %d') new_months,
coalesce(sum(t.products_ordered), 0) ordered_products
from months m
left join amazon_test t
on t.customer_id = m.customer_id
and t.order_date >= m.order_date
and t.order_date < m.order_date + interval 1 month
where m.customer_id = 1
group by m.customer_id, m.order_date
order by m.customer_id, m.order_date
I phrased the query so it actually operates on all customer_ids - if you remove the where clause in the outer query, you do get the results for all customers. If you really want the results for only one customer, you can optimize the query by pushing the where filter to the anchor of the recusive query.
Demo on DB Fiddle:
customer_id | new_months | ordered_products
----------: | :----------- | ---------------:
1 | January 31 | 3
1 | February 29 | 0
1 | March 31 | 1
1 | April 30 | 0
1 | May 31 | 0
1 | June 30 | 0
1 | July 31 | 0
1 | August 31 | 0
1 | September 30 | 1
1 | October 31 | 0
1 | November 30 | 0
1 | December 31 | 2
MySQL and PHP
I am attempting to write a SQL statement that will allow me to identify an individual that has not checked in this month. I am stuck on how to write the code.
I have 2 tables
tbl_cust c
tbl_attend a
SELECT
c.custRFID,
a.attendID,
a.RFID,
a.attendDate,
a.attendStatus,
a.attendStatusCode,
a.attendNotes
FROM
tbl_cust c RIGHT OUTER JOIN tbl_attend a ON c.custRFID = a.RFID
WHERE
MONTH(a.attendDate) <> MONTH(CURDATE()) AND YEAR(a.attendDate) =
YEAR(CURDATE())
The code filters out this month, however, it returns every date prior to this month. I only want to display the names from table c that don't have an entry in table a for this month. I don't need all the records.
-------------------------------------------------------------
ID RFID Date Time Status Status Code
-------------------------------------------------------------
1 01 02 03 E1 2018-01-03 1835 1 1
2 02 02 04 E2 2018-01-06 1235 1 1
3 01 02 03 E1 2018-02-07 1801 1 1
4 02 02 04 E2 2018-02-11 1109 1 1
5 01 02 03 E1 2018-03-03 1835 1 1
6 02 02 04 E2 2018-03-06 1235 1 1
7 01 02 03 E1 2018-04-07 1801 1 1
8 02 02 04 E2 2018-04-11 1109 1 1
9 01 02 03 E1 2018-05-01 1032 1 1
If this was May 16, 2018 the following should be the output:
-----------------------------------------------------------
ID RFID Date Time Status Status Code
-------------------------------------------------------------
8 02 02 04 E2 2018-04-11 1109 1 1
This is only using 2 customers, as the actual table will eventually have over 5,000 customers in it. with at least 1 entry per month. Some will have 3 of 4 entries per month, however, I only want to see those with no entry in the current calendar month. I.e.... If they checked in anytime during the calendar month of May 2018, they should not be displayed on the output.
Thank you in advance.
If you want someone who has ever checked in, then you can use aggregation:
select RFID
from attend a
group by RFID
having max(attendDate) < date_add(curdate(), interval 1 - day(curdate()) day);
I would write this as an EXISTS query:
SELECT
c.custRFID
FROM
tbl_cust c
WHERE NOT EXISTS (SELECT 1 FROM tbl_attend a
WHERE c.custRFID = a.RFID AND
MONTH(a.attendDate) = MONTH(CURDATE()) AND
YEAR(a.attendDate) = YEAR(CURDATE()));
You can do this using LEFT JOIN.
Also, try not to use YEAR(column) & MONTH(column) functions in ON/WHERE clauses as SQL will not be able to use indeces in this case.
Something like this should do the trick:
SELECT
c.custRFID,
a.attendID,
a.RFID,
a.attendDate,
a.attendStatus,
a.attendStatusCode,
a.attendNotes
FROM
tbl_cust c
LEFT JOIN tbl_attend a ON (
c.custRFID = a.RFID
AND a.attendDate > DATE_SUB(LAST_DAY(CURDATE()), INTERVAL 1 MONTH)
AND a.attendDate <= LAST_DAY(CURDATE())
)
WHERE
a.RFID IS NULL
I have two tables and trying to find records between start_date & end_date.
campaign table
campaign_id campaign_name start_date end_date
*********** ************* ********** ********
1 Deacon Navarro 2015-10-28 00:00:00 2015-10-31 00:00:00
2 Emily Oliver 2015-10-29 00:00:00 2015-11-04 00:00:00
statistic table
id campaign_id comments likes created_date
** *********** ******** ***** ************
1 1 14 24 2015-10-28 00:00:00
2 1 34 12 2015-10-29 00:00:00
3 1 23 12 2015-10-30 00:00:00
4 1 23 24 2015-10-31 00:00:00
5 1 21 45 2015-11-01 00:00:00
6 2 12 17 2015-10-31 00:00:00
7 2 23 12 2015-11-01 00:00:00
Now I want to find all records from statistic table where campaign_id=1 and created_date is between created_date to end_date from campaign table.
I need this output:
1 1 14 24 2015-10-28 00:00:00
2 1 34 12 2015-10-29 00:00:00
3 1 23 12 2015-10-30 00:00:00
4 1 23 24 2015-10-31 00:00:00
I have written very basic select query to find start_date & end_date from campaign table
SELECT start_date, end_date FROM campaign WHERE campaign_id = '1'
and I got this result:
start_date end_date
********** ********
2015-10-28 00:00:00 2015-10-31 00:00:00
but now I don't know how to find records from statistic table where created_date is between above start_date & end_date
Hope you are understand. I am not good with MySQL because I have just stared to learning so I need help If possible :)
Thanks.
Try this :
SELECT statistic.*
FROM statistic, campaign
WHERE
campaign.campaign_id = '1'
AND campaign.campaign_id = statistic.campaign_id
AND statistic.created_date BETWEEN campaign.start_date AND campaign.end_date
try this way
SELECT statistic.*
FROM statistic
INNER JOIN campaign ON statistic.campaign_id= campaign.campaign_id;
WHERE statistic.created_date BETWEEN campaign.start_date AND campaign.end_date
Try this :
SELECT s.*
FROM statistic AS s
INNER JOIN campaign AS c ON s.campaign_id = c.campaign_id;
WHERE c.campaign_id = 1
AND s.created_date BETWEEN c.start_date AND c.end_date
For more information about JOIN syntax here.