Alex_P
Alex_P

Reputation: 143

Matching Dates With SQL

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

Answers (3)

Schwern
Schwern

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

kc2018
kc2018

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

user8406805
user8406805

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);

Example:

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)

Demo

http://www.sqlfiddle.com/#!9/6ef0e5/11

Upvotes: 0

Related Questions