Reputation: 143
I have two different tables as shown in below and would like to Join them using two columns Product
and Date
. The Table1
has YYYYMMDD Date Format and Table2
has YYYYMM format. Here in the Table2
it the last business day of the month. Is there any way to Join those two tables.
Table1:
Product Date State
A 20080107 NY
A 20080131 TX
B 20100212 CT
B 20100226 MT
C 20150312 HG
C 20140425 UP
Table2:
Product Date Country
A 200801 USA
C 201503 AUS
B 201002 UK
B 201704 FIN
C 200605 IRE
A 200805 CAN
OUTPUT:
Product Date State Country
A 20080131 TX USA
B 20100226 MT UK
Upvotes: 0
Views: 1032
Reputation: 165414
If I understand correctly, you want all the rows in table1
where table1.date
corresponds with the last business day of the month in table2.date
.
I'm going to assume these are real date
columns, despite the formatting. If they're not, convert them to real dates. table1.date
is already in an acceptable YYYYMMDD
format so MySQL will convert it for you. table2.date
can be changed to an acceptable YYYYMMDD
format with concat(date, "01")
. Then you can use MySQL's date functions. I strongly suggest you make this conversion permanent by altering the tables, it will make everything simpler and faster.
MySQL doesn't have a last business day, but it does have last_day
and dayofweek
(and here is where I sigh about MySQL's inconsistent function name conventions). To get the last business day we find the last day of the month, then what day of the week it is, and subtract as appropriate.
Annoyingly, dayofweek
returns 1 for Sunday, not 0 which makes things a bit more difficult.
case
-- Sunday, move to Friday
when dayofweek(last_day('2010-02-01')) = 1 then last_day('2010-02-01') - 2
-- Saturday, move to Friday
when dayofweek(last_day('2010-02-01')) = 7 then last_day('2010-02-01') - 1
-- Weekday
when dayofweek(last_day('2010-02-01')) in(2,3,4,5,6) then last_day('2010-02-01')
else null
end as last_business_day;
This would be better done as a stored procedure. Not only does this avoid repetition because we can use variables, it also encapsulate the logic in a neat, testable function. We can also declare it deterministic
which means MySQL can cache the calculation for the same dates saving some CPU time.
create function last_business_day(date date)
returns date
deterministic
begin
declare last_day date;
declare last_dow int;
set last_day = last_day(date);
set last_dow = dayofweek(last_day);
return case
-- Sunday, move to Friday
when last_dow = 1 then
last_day - 2
-- Saturday, move to Friday
when last_dow = 7 then
last_day - 1
-- Weekday
when last_dow in(2,3,4,5,6) then
last_day
else
null
end;
end;
Now we can ask last_business_day('2010-02-01')
and get 2010-02-26
.
Armed with this, the query becomes simple.
select t1.product, t1.date, t1.state, t2.country
from table1 t1
join table2 t2 on t1.product = t2.product and
t1.date = last_business_day(t2.date)
Upvotes: 1
Reputation: 1460
As per OP, date
is stored an INT
. Find the last date of each month ( FLOOR(date/100)
) for each product on table1 and use that to join with table2.
select
t1.product,
t1.date,
t1.state,
t2.country
from
table2 t2
join
(select table1.*
from table1 join
(select product, max(date) lastdayOfMonth
from table1
group by product, floor(date/100)) t
on table1.product = t.product and table1.date = t.lastdayofMonth ) t1
on
t1.product = t2.product
and
t1.date = t2.date
Demo: sqlfiddle
Upvotes: 0
Reputation:
Here is your solved SQL:
select x.product,x.date,x.state,x.country
from
(select x.product,x.date,x.state,country
from (select product,max(date) date from table1 group by product) t1
join table2 on (left(t1.date,6) = table2.date)
join table1 x on (left(x.date,6) = left(t1.date,6))) x
join (select x.product,max(x.date) date
from (select product,max(date) date from table1 group by product) t1
join table2 on (left(t1.date,6) = table2.date)
join table1 x on (left(x.date,6) = left(t1.date,6))
group by x.product) y
on (x.product = y.product and x.date = y.date);
mysql> create table table1(product varchar(20),date varchar(8), state varchar(20));
Query OK, 0 rows affected (0.41 sec)
mysql> insert into table1 values
-> ('A','20080107','NY'),
-> ('A','20080131','TX'),
-> ('B','20100212','CT'),
-> ('B','20100226','MT'),
-> ('C','20150312','HG'),
-> ('C','20140425','UP');
Query OK, 6 rows affected (0.13 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql>
mysql> create table table2(Product varchar(20),Date varchar(8),Country varchar(20));
Query OK, 0 rows affected (0.33 sec)
mysql> insert into table2 values
-> ('A','200801','USA'),
-> ('C','201503','AUS'),
-> ('B','201002','UK'),
-> ('B','201704','FIN'),
-> ('C','200605','IRE'),
-> ('A','200805','CAN');
Query OK, 6 rows affected (0.13 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> select x.product,x.date,x.state,x.country
-> from
-> (select x.product,x.date,x.state,country
-> from (select product,max(date) date from table1 group by product) t1
-> join table2 on (left(t1.date,6) = table2.date)
-> join table1 x on (left(x.date,6) = left(t1.date,6))) x
-> join (select x.product,max(x.date) date
-> from (select product,max(date) date from table1 group by product) t1
-> join table2 on (left(t1.date,6) = table2.date)
-> join table1 x on (left(x.date,6) = left(t1.date,6))
-> group by x.product) y
-> on (x.product = y.product and x.date = y.date);
+---------+----------+-------+---------+
| product | date | state | country |
+---------+----------+-------+---------+
| A | 20080131 | TX | USA |
| B | 20100226 | MT | UK |
| C | 20150312 | HG | AUS |
+---------+----------+-------+---------+
3 rows in set (0.01 sec)
http://www.sqlfiddle.com/#!9/6ef0e5/11
Upvotes: 0