Сопоставление дат с SQL

0

У меня есть две разные таблицы, как показано ниже, и хотели бы присоединиться к ним, используя два столбца Product и Date. Table1 имеет YYYYMMDD Формат даты и Table2 имеет формат ГГГГММ. Здесь в Table2 это последний рабочий день месяца. Есть ли способ присоединиться к этим двум таблицам.

Таблица 1:

Product Date    State
A   20080107    NY
A   20080131    TX
B   20100212    CT
B   20100226    MT
C   20150312    HG
C   20140425    UP

Таблица 2:

Product Date    Country
A   200801  USA
C   201503  AUS
B   201002  UK
B   201704  FIN
C   200605  IRE
A   200805  CAN

ВЫХОД:

Product Date    State   Country
A   20080131    TX  USA
B   20100226    MT  UK
  • 1
    Что вы подразумеваете под "Таблица1 имеет формат даты ГГГГММДД"? Даты являются датами и не имеют никакого формата. Вы храните их как VARCHAR?
  • 0
    Что именно вы хотите сделать, чего не можете? Если они оба хранятся в виде даты, они сопоставимы независимо от «формата даты».
Показать ещё 15 комментариев
Теги:
datetime
date
left-join

3 ответа

1
Лучший ответ

Если я правильно понимаю, вы хотите, чтобы все строки в table1, где table1.date соответствует последний рабочий день месяца, в table2.date.

Я собираюсь предположить, что это настоящие столбцы date, несмотря на форматирование. Если это не так, переведите их в реальные даты. table1.date уже находится в приемлемом формате YYYYMMDD, поэтому MySQL будет конвертировать его для вас. table2.date может быть изменен до приемлемого формата YYYYMMDD с concat(date, "01"). Затем вы можете использовать функции даты MySQL. Я настоятельно рекомендую вам сделать это преобразование постоянным, изменив таблицы, это сделает все проще и быстрее.

У MySQL нет последнего рабочего дня, но он имеет last_day и dayofweek (и вот где я вздыхаю о несовместимых с MySQL соглашениях с именами функций). Чтобы получить последний рабочий день, мы найдем последний день месяца, затем в какой день недели и вычитаем по мере необходимости.

Раздражающе, dayofweek возвращает 1 для воскресенья, а не 0, что делает вещи немного сложнее.

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;

Это было бы лучше сделано в качестве хранимой процедуры. Это не только предотвращает повторение, потому что мы можем использовать переменные, но также инкапсулировать логику в аккуратной, проверяемой функции. Мы также можем объявить его deterministic что означает, что MySQL может кэшировать вычисления для тех же дат, сохраняя некоторое время процессора.

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;

Теперь мы можем запросить last_business_day('2010-02-01') и получить 2010-02-26.

Вооружившись этим, запрос становится простым.

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

Согласно OP, date хранится в INT. Найдите последнюю дату каждого месяца (FLOOR(date/100)) для каждого продукта в таблице1 и используйте его для соединения с таблицей2.

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 

Демо: sqlfiddle

  • 0
    Здесь моя колонка Дата в формате Integer. Таким образом, приведенный выше код не работает
  • 0
    @Alex_P Пересмотрено решение для работы с датой, являющейся целочисленным типом данных.
0

Вот ваш решаемый 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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню