2013-11-01 6 views
0

У меня есть таблица под названием Trucks с двумя столбцами даты: Arrival и Released. Я могу рассчитать среднее количество дней между этими датами, как так:SQLite: Получить средние дни недели между двумя датами

SELECT avg(julianday(released) - julianday(arrival)) 
FROM Trucks 

Однако, я только хочу, чтобы рассчитывать дни недели - то есть, я хочу, чтобы игнорировать субботу и воскресенье. Есть ли способ сделать это в SQLite? Я видел решения для более надежных РСУБД, таких как Oracle и MSSQL, но ни один из них не работает для SQLite.

ответ

3

должна быть отрегулирована Необработанная разница в дни в зависимости от (только) на неделе дни arrival и released дней:

rel arr|0 1 2 3 4 5 6 
-------+-+-+-+-+-+-+- 
    0 |2 0 0 0 0 0 1 
    1 |2 2 0 0 0 0 1 
    2 |2 2 2 0 0 0 1 
    3 |2 2 2 2 0 0 1 
    4 |2 2 2 2 2 0 1 
    5 |2 2 2 2 2 2 1 
    6 |2 2 2 2 2 2 2 

Это число может быть вычислено с помощью простого выражения (здесь: внутреннее выражение СЛУЧАЯ):

SELECT 
AVG(julianday(released) - julianday(arrival) - 
    CASE WHEN julianday(released) = julianday(arrival) THEN 0 
    ELSE (CAST((julianday(released) - julianday(arrival))/7 AS INTEGER) * 2 
     ) + 
     CASE WHEN strftime('%w', arrival) <= strftime('%w', released) THEN 2 
      ELSE strftime('%w', arrival) = '6' 
     END 
    END) 
FROM trucks 

(логическое выражение как x='6' возвращает 0 или 1.)

+0

Mind = взорван. Это уменьшило мой запрос на 635 строк до 97 строк. (Логика выходного дня повторно используется несколько раз в более крупном запросе.) Спасибо человеку! – penco

+0

В качестве небольшого добавления мне пришлось обернуть 'MAX()' вокруг оператора AVG(), потому что изредка получаю отрицательные числа из-за проблем округления. Таким образом, это закончилось как «MAX (AVG (...), 0)». – penco

1

Хорошо, я выяснил очень грязное решение, используя множество вложенных операторов CASE. Он проверяет день недели released, а затем номер дня недели arrival и делает расчет, чтобы выяснить, сколько недель прошло. После этого я добавляю 0, 1 или 2 в качестве базового числа выходных дней, которое должно было пройти между этими двумя днями (т.е. с пятницы по понедельник всегда +2 выходных дня, даже если прошло менее недели даты).

Здесь он находится на всякий случай, если кто-либо найдет его полезным. Вполне возможно, самый уродливый SQL, который я когда-либо писал. Если кто-нибудь выяснит лучший способ, пожалуйста, дайте мне знать.

(под редакцией для простоты на основе обратной связи CL в)

SELECT 
avg(
julianday(released) - julianday(arrival) - 

CASE WHEN julianday(released) = julianday(arrival) THEN 0 ELSE 
CASE strftime('%w', released) 
WHEN '0' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)  
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 1) 
END 
WHEN '1' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2) 
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 1) 
END 
WHEN '2' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)  
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)    
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)    
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)  
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 1) 
END 
WHEN '3' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)  
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)    
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)    
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 1) 
END 
WHEN '4' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)  
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 0)   
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 1) 
END 
WHEN '5' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)  
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 1) 
END 
WHEN '6' THEN 
CASE strftime('%w', arrival) 
WHEN '0' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)  
WHEN '1' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)   
WHEN '2' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '3' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '4' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '5' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2)    
WHEN '6' THEN ((cast((julianday(released)-julianday(arrival))/7 as int) * 2) + 2) 
END 
END 
END 

), avg(julianday(released)-julianday(arrival)) 
from trucks 

(Замечание: при avg(julianday(released)-julianday(arrival)) в конце концов, это только для целей тестирования, чтобы показать, что новое расчетное среднее значение на самом деле меньше, чем прямой среднее значение разница между двумя датами).

+1

A [СЛУЧАЙ выражение] (http://www.sqlite.org/lang_expr.html#case) может имеют несколько ветвей WHEN. В этом случае проверяемое выражение может быть записано один раз между CASE и WHEN. –

+0

Спасибо! Я реорганизовал код для использования ветвящейся структуры CASE. Намного легче управлять сейчас. – penco

0

Я нашел этот пост во время поиска чтобы увидеть, имеет ли SQLite функцию дня недели. Если вы используете верхний ответ, вряд ли вы получите очень сильное значение в течение нескольких дней недели. Вот код, который я использовал для моего проекта, который вычисляет трудодни между созданием билета и закрытием в Spiceworks:

SELECT t.id 
,t.summary 
,t.category 
,u.email 
,(CAST(strftime('%j', t.closed_at) as INTEGER) - CAST(strftime('%j', t.created_at) as INTEGER) - /*I can't figure it out, but julianday() wasn't giving me the correct distances for some numbers. I used the day of the year, instead, which resolved things as expected*/ 
    CASE              
    WHEN CAST(strftime('%W', t.closed_at) as INTEGER) - CAST(strftime('%W', t.created_at) as INTEGER) = 0 THEN 0 /*If they are in the same week then there is no weekend to count. If you closed a ticket, that was opened on a Monday, on Saturday, then you must of worked on Saturday and therefore counts as work day the same as if you finished it on Monday*/ 
    WHEN CAST(strftime('%w', t.created_at) as INTEGER) = 0 THEN (CAST(strftime('%W', t.closed_at) as INTEGER) - CAST(strftime('%W', t.created_at) as INTEGER)) * 2 - 1 /*If they made their ticket on Sunday, don't count that Sunday*/ 
    WHEN CAST(strftime('%w', t.created_at) as INTEGER) = 6 THEN (CAST(strftime('%W', t.closed_at) as INTEGER) - CAST(strftime('%W', t.created_at) as INTEGER)) * 2 - 2 /*If they made their ticket on Saturday, don't count that Saturday*/ 
    ELSE (CAST(strftime('%W', t.closed_at) as INTEGER) - CAST(strftime('%W', t.created_at) as INTEGER)) * 2 /*Ignoring the possibility for closed dates that happen before open dates, take the number of weeks between each date and assume each had a Saturday and Sunday within them*/ 
    END) as 'Week Days Elapsed' /*This equation in full represents: Full_Distance_In_Days_Between - Number of weekend days calculated*/ 
    FROM Tickets as t 
    LEFT JOIN Users as u 
    on t.created_by = u.id 
    WHERE strftime('%Y-%m', t.created_at) = strftime('%Y-%m', 'now') 
Смежные вопросы