2009-05-11 8 views
2

Мне нужно рассчитать все счета-фактуры, которые были оплачены в первые «N» дней месяца. У меня есть две таблицыРасчет в SQL первый рабочий день данного месяца

. INVOICE: содержит информацию о счете. Единственное поле, которое имеет значение, называется «datePayment»

. HOLYDAYS: Это таблица с одним столбцом. Записи в этой таблице, имеют вид «2009-01-01», 2009-05-01" и так далее.

следует рассмотреть также по субботам и воскресеньям (это может быть не проблема, потому что я мог вставить эти дни в Hollidays таблицы для того, чтобы рассматривать их как Hollidays если есть необходимость)

проблема заключается в том, чтобы вычислить, который является "предел оплаты.

select count(*) from invoice 
where datePayment < PAYMENTLIMIT 

Мой вопрос заключается в том, чтобы вычислить эту PAYMENTLIMIT. Где PAYMENTLIMIT - «пятый рабочий день каждого месяца».

Запрос должен выполняться под Mysql и Oracle, поэтому должен использоваться стандартный SQL.

Подсказка?

EDIT Для того, чтобы соответствовать названию вопроса псевдо-запрос должен считанному следующим образом:

select count(*) from invoice 
where datePayment < FIRST_WORKING_DAY + N 

тогда вопрос может быть уменьшен, чтобы вычислить FIRST_WORKING_DAY каждого месяца.

+2

Правильное написание вашей второй таблицы «праздник» –

+2

Фактически таблица называется «Feiertag». :-) В любом случае, спасибо за комментарий. исправит его. – Luixv

+0

У меня возникли проблемы с пониманием вашего вопроса. Вы хотите узнать, как найти первый рабочий день за каждый месяц или какой лимит оплаты? Лимит оплаты в первый рабочий день + 5 рабочих дней? –

ответ

2

Нечто подобное может работать:

create function dbo.GetFirstWorkdayOfMonth(@Year INT, @Month INT) 
returns DATETIME 
as begin 
    declare @firstOfMonth VARCHAR(20) 
    SET @firstOfMonth = CAST(@Year AS VARCHAR(4)) + '-' + CAST(@Month AS VARCHAR) + '-01' 

    declare @currDate DATETIME 
    set @currDate = CAST(@firstOfMonth as DATETIME) 

    declare @weekday INT 
    set @weekday = DATEPART(weekday, @currdate) 

    -- 7 = saturday, 1 = sunday 
    while @weekday = 1 OR @weekday = 7 
    begin 
     set @currDate = DATEADD(DAY, 1, @currDate) 
     set @weekday = DATEPART(weekday, @currdate) 
    end 

    return @currdate 
end 

Я не 100% уверен, о том, фиксированы числа «День недели» или может зависеть от вашего местоположения на вашем SQL Server. Проверьте это!

Марк

1

У нас есть таблица дат в нашем приложении (заполняется со всеми датами и датой частями на несколько десятков лет), что позволяет различную «недостающую» дата манипуляции, как (в псевдо-SQL):

select min(ourdates.datevalue) 
from ourdates 
where ourdates.year=<given year> and ourdates.month=<given month> 
    and ourdates.isworkday 
    and not exists (
     select * from holidays 
     where holidays.datevalue=ourdates.datevalue 
    ) 
4

вы можете посмотреть в первый день в течение месяца, когда дата не в праздничном столе и дата не выходной:

select min(datePayment), datepart(mm, datePayment) 
from invoices 
where datepart(dw, datePayment) not in (1,7) --day of week 
and not exists (select holiday from holidays where holiday = datePayment) 
group by datepart(mm, datePayment) --monthnr 
+1

или наоборот: предварительно вычислите все первые рабочие дни в месяц и сохраните их в таблице. поэтому вам нужно только запросить эту таблицу. – Scoregraphic

1

Хорошо, на первом уколе, вы могли бы поместите следующий код в UDF и перейдите в Год и Месяц в качестве переменных. Затем он может вернуть TestDate, который является первым рабочим днем ​​месяца.

DECLARE @Month INT 
DECLARE @Year INT 

SELECT @Month = 5 
SELECT @Year = 2009 

DECLARE @FirstDate DATETIME 
SELECT @FirstDate = CONVERT(varchar(4), @Year) + '-' + CONVERT(varchar(2), @Month) + '-' + '01 00:00:00.000' 

DROP TABLE #HOLIDAYS 
CREATE TABLE #HOLIDAYS (HOLIDAY DateTime) 

INSERT INTO #HOLIDAYS VALUES('2009-01-01 00:00:00.000') 
INSERT INTO #HOLIDAYS VALUES('2009-05-01 00:00:00.000') 

DECLARE @DateFound BIT 
SELECT @DateFound = 0 
WHILE(@DateFound = 0) 
BEGIN 
    IF(
     DATEPART(dw, @FirstDate) = 1 
     OR 
     DATEPART(dw, @FirstDate) = 1 
     OR 
     EXISTS(SELECT * FROM #HOLIDAYS WHERE HOLIDAY = @FirstDate) 
    ) 
    BEGIN 
     SET @FirstDate = DATEADD(dd, 1, @FirstDate) 
    END 
    ELSE 
    BEGIN 
     SET @DateFound = 1 
    END 
END 

SELECT @FirstDate 

Вещи, которые я не люблю с этим решением, хотя и являются, если таблица содержит праздники все дни месяца будет бесконечный цикл. (Вы можете проверить, что цикл все еще смотрит на правильный месяц). Он полагается на равные даты, например, все время 00:00:00. Наконец, способ, которым я вычислял 1-й месяц прошлого в использовании конкатенации строк, был коротким. Есть намного лучшие способы поиска фактического первого дня месяца.

1

Получает первый N рабочих дней каждого месяца 2009 года:

select * from invoices as x 
where 
    datePayment between '2009-01-01' and '2009-12-31' 


    and exists 
    ( 
     select    
       1 
     from invoices 
     where 
      -- exclude holidays and sunday saturday... 
      (
       datepart(dw, datePayment) not in (1,7) -- day of week 


       /* 
       -- Postgresql and Oracle have programmer-friendly IN clause 
       and 
       (datepart(yyyy,datePayment), datepart(mm,datePayment)) 
       not in (select hyear, hday from holidays) 
       */ 


       -- this is the MSSQL equivalent of programmer-friendly IN 
       and 
       not exists 
       (
        select * from holidays 
        where 
         hyear = datepart(yyyy,datePayment) 
         and hmonth = datepart(mm, datePayment) 
       )         
      ) 
      -- ...exclude holidays and sunday saturday 



      -- get the month of x datePayment 
      and     
      (datepart(yyyy, datePayment) = datepart(yyyy, x.datePayment) 
      and datepart(mm, datePayment) = datepart(mm, x.datePayment)) 


     group by 
      datepart(yyyy, datePayment), datepart(mm, datePayment)  

     having 
      x.datePayment < MIN(datePayment) + @N -- up to N working days 
    ) 
2

Вместо таблицы Holidays дней, чтобы исключить, мы используем подход календарь таблицы: одну строку на каждый день приложение будет когда-либо потребность (тридцать лет охватывает скромные 11K строк). Таким образом, он имеет не только столбец is_weekday, но и другие вещи, относящиеся к предприятию, например. julianized_date. Таким образом, каждая возможная дата будет иметь готовое готовое значение для first_working_day_this_month, и поиск этого требует простого поиска (какие SQL-продукты имеют тенденцию оптимизироваться для!), А не «вычислять» его каждый раз «на лету».

1

Возвращает первый понедельник текущего месяца

SELECT DATEADD(
    WEEK, 
    DATEDIFF(--x weeks between 1900-01-01 (Monday) and inner result 
     WEEK, 
     0, --1900-01-01 
     DATEADD(--inner result 
      DAY, 
      6 - DATEPART(DAY, GETDATE()), 
      GETDATE() 
     ) 
    ), 
    0 --1900-01-01 (Monday) 
) 
+0

Это был не ответ, но в любом случае это хороший намек. –

0
SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0) -1)/7*7 + 7, 0); 
0
select if(weekday('yyyy-mm-01') < 5,'yyyy-mm-01',if(weekday('yyyy-mm-02') < 5,'yyyy-mm-02','yyyy-mm-03')) 

субботы и воскресенья 5, 6, так что вам нужно только две проверки, чтобы получить первый рабочий день