2014-11-03 3 views
4

나는 레스토랑에 대한 opening_hours있는 테이블이 있습니다SQL "먼저 관련 일"

SELECT * FROM opening_hours; 
+----+---------------+------------+----------+-----+ 
| id | restaurant_id | start_time | end_time | day | 
+----+---------------+------------+----------+-----+ 
| 1 |    1 | 12:00:00 | 18:00:00 | 1 | 
| 2 |    1 | 09:00:00 | 19:00:00 | 4 | 
| 3 |    2 | 09:00:00 | 16:00:00 | 4 | 
| 4 |    2 | 09:00:00 | 16:00:00 | 5 | 
| 5 |    3 | 09:00:00 | 16:00:00 | 4 | 
| 6 |    3 | 09:00:00 | 16:00:00 | 5 | 
| 7 |    3 | 09:00:00 | 16:00:00 | 1 | 
| 8 |    3 | 09:00:00 | 16:00:00 | 6 | 
+----+---------------+------------+----------+-----+ 

http://www.sqlfiddle.com/#!2/eaea09/1

가 지금은 현재에 대한 모든 레스토랑에 대해 "가장 가까운"다음 날 또는 같은 날 가져 오려는 일. 나는이 작업을 수행 할 수있는 일 1의 경우

restaurant_id: 1 day: 1 
restaurant_id: 2 day: 4 
restaurant_id: 3 day: 1 

: 현재 하루 1 예를 들어 결과가 될 것

SELECT day FROM opening_hours WHERE day >= 1 GROUP BY restaurant_id LIMIT 1 

을하지만 오늘은 6이 될 것입니다 경우 그 작동하지 않을 것입니다. 나는 일 (7)의 최대 숫자를 얻고 그 것이 찾아 낼 수없는 경우에 찾아내는 질문이 1에서 다시 시도 시작해야 할 것입니다. 그래서 하루 6에 대한 결과는이 경우에있을 것입니다 :

는 는
restaurant_id: 1 day: 1 
restaurant_id: 2 day: 4 
restaurant_id: 3 day: 6 

어떻게 쿼리에이를 수 있을까?

나는 의사 SQL에서 이런 일이 될 수 있다고 생각 것 :

SELECT `day` FROM opening_hours WHERE `day` >= 'today' IF NOT FOUND WHERE `day` >= 1 GROUP BY `restaurant_id` LIMIT 1 

편집 :

내가이 쿼리를 실행하고, 레스토랑 일치하는 항목이 처음 발견되었을 경우 결정할 수있다 . 그렇지 않은 경우 두 번째 쿼리를 실행하십시오. 그러나 더 좋은 방법이 있어야합니다. 그런

답변

2

테스트 됨,이 하나의 작동 :) 당신은 간단하게 유지할 수 있습니다.

검색어 :

SELECT * FROM (
    SELECT 
    oh.* 
    FROM 
    opening_hours oh 
    ORDER BY restaurant_id, 
    `day` + IF(`day` < $current_day, 7, 0) 
) sq 
GROUP BY restaurant_id; 

설명 :하지만

참고이 조금 해키입니다. group by에서 사용되지 않는 집계 함수가 적용되지 않은 열을 선택하려면 일반적으로 허용되지 않습니다. 왜냐하면 이론적으로 각 그룹의 임의의 행을 줄 수 있기 때문입니다. 이것이 대부분의 데이터베이스 시스템에서는 허용되지 않는 이유입니다. MySQL은 실제로 내가 아는 유일한 것입니다 (SQL 모드를 통해 달리 설정되지 않은 경우). 내가 말했듯이, 이론적으로. 실제로는 약간 다르며 하위 쿼리에서 order by을 수행하면 MySQL은 항상 정렬 순서에 따라 최소 또는 최대 값을 제공합니다.

테스트 : 현재 날짜와

원하는 결과 = 1 :

[email protected]:playground > SELECT * FROM (
    ->  SELECT 
    ->  oh.* 
    ->  FROM 
    ->  opening_hours oh 
    ->  ORDER BY restaurant_id, 
    ->  `day` + IF(`day` < 1, 7, 0) 
    ->) sq 
    -> GROUP BY restaurant_id; 
+----+---------------+------------+----------+-----+ 
| id | restaurant_id | start_time | end_time | day | 
+----+---------------+------------+----------+-----+ 
| 1 |    1 | 12:00:00 | 18:00:00 | 1 | 
| 3 |    2 | 09:00:00 | 16:00:00 | 4 | 
| 7 |    3 | 09:00:00 | 16:00:00 | 1 | 
+----+---------------+------------+----------+-----+ 
3 rows in set (0.00 sec) 
현재 일 = 6

원하는 결과 :

[email protected]:playground > SELECT * FROM (
    ->  SELECT 
    ->  oh.* 
    ->  FROM 
    ->  opening_hours oh 
    ->  ORDER BY restaurant_id, 
    ->  `day` + IF(`day` < 6, 7, 0) 
    ->) sq 
    -> GROUP BY restaurant_id; 
+----+---------------+------------+----------+-----+ 
| id | restaurant_id | start_time | end_time | day | 
+----+---------------+------------+----------+-----+ 
| 1 |    1 | 12:00:00 | 18:00:00 | 1 | 
| 3 |    2 | 09:00:00 | 16:00:00 | 4 | 
| 8 |    3 | 09:00:00 | 16:00:00 | 6 | 
+----+---------------+------------+----------+-----+ 
3 rows in set (0.00 sec) 
+0

나는 이것을 좋아한다. <3 잘 작동하는 것 같습니다. – Matthijn

1

뭔가를 수행해야합니다

SELECT oh1.* 
-- set the start day 
FROM (SELECT @start := 1) AS start, 
-- calculate difference in days 
    (SELECT *, (CASE WHEN [email protected] >= 0 THEN [email protected] ELSE [email protected]+7 END) AS diff 
     FROM opening_hours) AS oh1 
-- find minimum difference 
JOIN (SELECT restaurant_id, MIN(CASE WHEN [email protected] >= 0 THEN [email protected] ELSE [email protected]+7 END) AS min_diff 
     FROM opening_hours 
     GROUP BY restaurant_id) AS oh2 
    ON oh1.restaurant_id = oh2.restaurant_id AND 
    oh1.diff = oh2.min_diff 

당신의 시작 일 또는 전화로 @start := 1 교체 DAYOFWEEK(CURDATE())에, 당신이 그것을 수행하는 방법에 따라!

1

이것은 까다로운 내용입니다.

최고입니다. 작동하는 것처럼 보이지만 일부 가장자리가 누락 될 수 있습니다.

SELECT sub0.restaurant_id, MIN(sub1.day) 
FROM 
(
    SELECT restaurant_id, MIN(LEAST(ABS(DAYOFWEEK(CURDATE()) - day), ABS(DAYOFWEEK(CURDATE()) - (day + 7)), ABS(DAYOFWEEK(CURDATE()) - (day - 7)))) AS difference 
    FROM opening_hours 
    GROUP BY restaurant_id 
) sub0 
INNER JOIN 
(
    SELECT restaurant_id, day, LEAST(ABS(DAYOFWEEK(CURDATE()) - day), ABS(DAYOFWEEK(CURDATE()) - (day + 7)), ABS(DAYOFWEEK(CURDATE()) - (day - 7))) AS difference 
    FROM opening_hours 
) sub1 
ON sub0.restaurant_id = sub1.restaurant_id 
AND sub0.difference = sub1.difference 
GROUP BY sub0.restaurant_id 

첫 번째 하위 쿼리는 오늘 날짜와 각 레스토랑 데이 사이의 절대적인 차이를 얻고 있습니다. 그것은 일, 일 플러스 7, 일 마이너스 7을 사용하여 ABS를 사용하여 일의 차이를 얻고 LEAST를 사용하여 가장 낮은 차이를 얻습니다. 이 방법은 현재 날짜가 1이고 식당 일이 6 일 경우 1 + 7을 6, 1 - 7을 6, 1을 6으로 비교하고 그 중 가장 적은 것을 얻는 것입니다 (이 경우 1 + 7이됩니다).).

두 번째 하위 쿼리는 각 가능한 레스토랑/일에 대해 그 차이와 요일을 가져오고 첫 번째 하위 쿼리에 합류합니다.

외부 쿼리는 MIN을 사용하여 2가 바로 가까운 날을 선택합니다.

1

첫 단계는 단순히 7을 추가하는 것 하루가 검색 한 날짜보다 낮을 경우 일일 차이 계산 결과 :

,210
SET @Day = 6; 
SELECT ID, Restaurant_id, Day, 
     CASE WHEN Day < @Day THEN 7 ELSE 0 END + Day - @Day AS DaysFromNow 
FROM opening_hours; 

이 제공됩니다

ID RESTAURANT_ID DAY  DAYSFROMNOW 
1 1    1  2 
2 1    4  5 
3 2    4  5 
4 2    5  6 
5 3    4  5 
6 3    5  6 
7 3    1  2 
8 3    6  0 

는 그런 다음, 각 레스토랑의 최소 DaysFromNow을 얻을 필요가 다음 relavant 일 얻으려면 기본 테이블로 다시 조인

SET @Day = 6; 
SELECT o.* 
FROM opening_hours AS o 
     INNER JOIN 
     ( SELECT Restaurant_id, 
        MIN(CASE WHEN Day < @Day THEN 7 ELSE 0 END + Day - @Day) AS DaysFromNow 
      FROM opening_hours 
      GROUP BY Restaurant_id 
     ) AS mo 
      ON mo.Restaurant_id = o.Restaurant_id 
      AND mo.DaysFromNow = (CASE WHEN Day < @Day THEN 7 ELSE 0 END + Day - @Day); 

Example on SQL Fiddle