SQL-запрос для получения ежедневных платежей за месяц, даже если в данный день нет платежей - PullRequest
0 голосов
/ 03 декабря 2009

Я использую SQL Server 2005 и пытаюсь написать запрос, в котором я хочу получить платежи за определенный месяц. В настоящее время у меня есть:

select sum(p1.paymentamount) as subtotal, 
       CONVERT(char(10), p1.paymentdate, 103) as paymentdate
  from tblpayment p1
 where 1=1
   and p1.paymentdate >= @fromdate
   and p1.paymentdate <= @todate
group by p1.paymentdate
order by p1.paymentdate

Схема:

CREATE TABLE [dbo].[tblPayment]
( 
    [paymentid] [int] IDENTITY(1,1) NOT NULL, 
    [userid] [int] NULL , 
    [paymentdate] [datetime] NOT NULL, 
    [paymentamount] [int] NULL, 
    [paymenttype] [varchar](50) NULL, 
    [paymentnotes] [varchar](200) NULL, 
    [paymentcurrency] [nchar](10) NULL 
) 

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

Есть еще одна загвоздка. Валюта платежей отличается. Итак, как я могу получить еще один столбец в запросе, который дает мне eurototal и sterlingtotal на основе переданного параметра @currency? Предполагая, что в таблице есть столбец для "платежной валюты"

Ответы [ 5 ]

1 голос
/ 03 декабря 2009

Вот один подход

Создайте следующую функцию:

CREATE FUNCTION [dbo].[DateTable] (@StartDate DATETIME, @endDate DATETIME)
RETURNS @Itms TABLE
(
    TheDate DATETIME
)
AS
BEGIN
    DECLARE @theDate DATETIME

    SET @TheDate = @StartDate
    WHILE @TheDate <= @endDate
    BEGIN
        INSERT @Itms VALUES (@theDate)
        SET @TheDate =dateAdd(d,1,@theDate)
    END
    RETURN
END;

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

select sum(p1.paymentamount) as subtotal, 
       CONVERT(char(10), p1.paymentdate, 103) as paymentdate
  from 
    (select * from tblpayment p1
      where 1=1
      and p1.paymentdate >= @fromDate
      and p1.paymentdate <= @toDate
     union
        select theDate as paymentDate,0 as paymentAmount 
          from dbo.dateTable (@fromDate,@toDate)
    ) p1
group by p1.paymentdate
1 голос
/ 03 декабря 2009

Если в tblPayment нет фиктивных записей для дат без какой-либо оплаты, эти даты не будут отображаться в запросе, который выбирается только из tblPayment.

Я справляюсь с этим, создавая отдельную таблицу, в которой ничего нет, кроме дат (по одной строке на дату), проверяя, чтобы у меня были все даты, чтобы покрыть мой запрос, а затем СЛЕДУЮЩУЮ СОЕДИНЕНИЕ с моей основной таблицей (в данном случае tblPayment) в таблице дат:

SELECT * FROM tblPayment LEFT OUTER JOIN tblDates
ON tblPayment.PaymentDate = tblDates.PossibleDate

Эту основную идею можно усовершенствовать с помощью GROUP BY, чтобы получить требуемые итоговые цифры.

1 голос
/ 03 декабря 2009

Вы должны работать в обратном направлении. Чтобы получить строки для несуществующих дат, необходимо внешнее соединение их со строками, которые имеют эти даты. Для внешнего соединения необходимо иметь последовательность, к которой нужно присоединиться. Поскольку у вас нет последовательности, вам нужно ее создать.

Чтобы создать эту последовательность, у вас есть два варианта:

  • Создать статическую последовательность дат и сохранить ее в постоянной таблице (ответ Ларри); или
  • Используйте существующую числовую последовательность (например, spt_values), чтобы создать ее на лету.

Предположим, вам нужна гибкость второго подхода. Вот общий фрагмент, который я использую для таких вещей:

SELECT DATEADD(DAY, v.number, @fromdate)
FROM master.dbo.spt_values v
WHERE v.type = 'P'
AND v.number <= DATEDIFF(DAY, @fromdate, @todate)

Теперь просто добавьте это в CTE и присоединитесь к нему:

WITH Dates_CTE (dt) AS
(
    -- // Paste the snippet above in here
)
SELECT d.dt AS paymentdate, ISNULL(SUM(p.paymentamount), 0) AS subtotal
FROM Dates_CTE d
LEFT JOIN tblpayment p
ON p.paymentdate = d.dt
GROUP BY d.dt
ORDER BY d.dt

(Обновление: я пропустил предложение WHERE в основном запросе, поскольку он технически обрабатывается соединением, но в некоторых случаях вы можете повысить производительность, оставив его в)

Что касается конвертации валюты, посмотрите синтаксис PIVOT.


Обновление в PIVOT: вы можете просто заключить весь запрос в скобки, а затем:

SELECT paymentdate, [Euro] AS euroamount, [Pound] as poundamount
FROM
(
    -- // Insert the full query from above in here
) p
PIVOT
(
    SUM(subtotal)
    FOR paymentcurrency IN ([Euro], [Pound])
) AS pvt

Трудно проверить, не зная точно, какие данные там находятся, но попробуйте это как отправную точку.

0 голосов
/ 04 декабря 2009

Как упоминалось ранее, вы должны использовать отдельную таблицу (временную или постоянную). Конвертация валюты может быть сделана с использованием выписки CASE. Проверьте ниже (я составил коэффициенты пересчета;)

declare @dates table (dateitem datetime)
declare @lower datetime
declare @upper datetime
set @lower = '12/1/9'
set @upper = '12/31/9'

while @lower <= @upper
  begin
    insert into  @dates values (@lower)
    set @lower = dateadd(day, 1, @lower)
  end


select dateitem, paymentcurrency,
   paymentindollars = case paymentcurrency when 'dollars' then total when 'euro' then total * 1.7 else 0 end,
   paymentineuros = case paymentcurrency when 'dollars' then total * 0.73 when 'euro' then total else 0 end
   from 
   (select dateitem, paymentcurrency, sum(paymentamount) as total
    from @dates DT left join tblpayment on DT.dateitem = tblpayment.paymentdate group by dateitem, paymentcurrency
    ) IQ order by dateitem

Предостережения, на которые следует обратить внимание:

  • ваша дата платежа может быть это то, что вам придется удалить (через кастинг) для правильной работы объединения / группировки
  • для правильной конвертации вы должны разделить валюты разных типов, вы всегда можете заключить их в другой sql, чтобы получить итоговую сумму за день
  • конвертация валюты, как правило, хороша только в течение дня, поэтому применение общей конвертации за определенный период времени не даст вам хороших финансовых результатов, только приличные приблизительные показатели (т.е. не пытайтесь подать это на свои налоги;)

Надеюсь, это немного поможет.

0 голосов
/ 03 декабря 2009

попробовать что-нибудь подобное, возможно?

select sum(p1.paymentamount) as subtotal, 
       CASE WHEN (CONVERT(char(10), p1.paymentdate, 103) = 0) THEN 'No Sale'
       ELSE 
        CONVERT(char(10), p1.paymentdate, 103)

       END as paymentdate 
FROM   tblpayment
where  paymentdate BETWEEN @fromdate and @todate
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...