2010-04-18 3 views
1

Я ищу помощь, чтобы создать запрос, чтобы решить следующие задачи:SQL запрос, чтобы показать то, что было уделено каждый месяц

Давайте представим себе ряд:

Name StartDate EndDate  Paid 
James 10-10-2010 17-02-2011 860 

И Heres схема для таблицы по запросу:

payment_details (name VARCHAR(50) NOT NULL, 
       start_date DATETIME NOT NULL, 
       end_date DATETIME NOT NULL, 
       paidFLOAT NOT NULL) 

Теперь мне нужен способ, чтобы разделить эту строку, так что я могу видеть, что он ра YS каждый месяц, за период, запрос, который возвращает:

Name Year Month Paid 
James 2010 10  172 
James 2010 11  172 
James 2010 12  172 
James 2011 01  172 
James 2011 02  172 

Есть много разных клиентов с различными StartDate/EndDate и суммами оплаченных, поэтому запрос должен справиться с этой Aswell. Как это сделать в SQL (MS SQL Server 2005)?

Помощь будет очень признательна!

+0

PLZ описать таблицу, которая содержит данные платежа ... – TheCodeArtist

+0

Вопрос обновлен. –

+0

Возможно, вы захотите исправить написание последнего столбца. http://www.wsu.edu/~brians/errors/payed.html – David

ответ

1

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

select p.name, d.year, d.month, p.paid/(datediff(m, p.startdate, p.enddate) + 1) 
from (
    select year(date) as year, month(date) as month, min(date) as monthbegin, max(date) as monthend 
    from datestable 
    group by year(date), month(date) 
) d 
left join payment_detail p on d.monthbegin<p.enddate and d.monthend>p.startdate 

Надеюсь, что у меня есть условия подключения прямо, не стесняйтесь исправить (у меня нет возможности проверить здесь).

+0

Также ознакомьтесь с статьей Джеффа Модена «Использование таблицы таблиц» для создания последовательности дат очень быстро. Я много использовал эту технику и очень быстро создал последовательность дат. Ссылка требует бесплатной подписки. http://www.sqlservercentral.com/articles/T-SQL/62867/ – Jeremy

+0

Спасибо за ссылку, к сожалению, мне не нравятся подписки только для чтения контента :( – Arvo

+0

Я успешно реализовал ваше решение. Большое спасибо! –

0

О единственным способом я могу видеть сделать это в прямой SQL, чтобы иметь другую таблицу называют периоды, содержащие все годы и месяцы, а именно:

Year Month 
2010  10 
2010  11 
2010  12 
2011  1 
2011  2 
2011  3 
2011  4 

и так далее ...

Теперь вы присоединиться к детали оплаты таблицы с этой таблицей, как так:

SELECT PD.[name], Per.Year, Per.Month, 
    PD.payed/DateDiff(mm, PD.start_date, PD.end_date) AS Payed 
FROM payment_details PD 
    INNER JOIN Periods Per On 
     Per.Year >= DatePart(yy, start_date) AND Per.Month >= DatePart(mm, start_date) 
    AND Per.Year <= DatePart(yy, end_date) AND Per.Month <= DatePart(mm, end_date) 
ORDER BY PD.[name], Per.Year, Per.Month 

в противном случае вам, возможно, потребуется создать хранимую процедуру, и шаг за шагом через несколько месяцев и CRE съели полученный вами набор данных «на лету».

3

Вам нужно:

  • генерировать строку для каждого месяца для каждого имени (используйте таблицу Numbers)
  • генерировать разность месяца в качестве делителя (DATEDIFF использует конец месяца как месяц граница)
  • предположить end_date> = start_date (у вас есть СНЕСК?), чтобы избежать деления на ноль ошибок

вы, возможно, придется настроить это, конечно ...

--DROP TABLE #numbers 
--DROP TABLE #payment_details 
CREATE TABLE #numbers (num smallint PRIMARY KEY) 

CREATE TABLE #payment_details (name VARCHAR(50) NOT NULL, 
       start_date DATETIME NOT NULL, 
       end_date DATETIME NOT NULL, 
       paid FLOAT NOT NULL) 

INSERT #payment_details VALUES ('James', '20101010', '20110217', 860) 
INSERT #payment_details VALUES ('Jane', '20101110', '20110117', 900) 
INSERT #payment_details VALUES ('John', '20101128', '20101128', 500) 

INSERT #numbers 
SELECT TOP 1000 
    ROW_NUMBER() OVER (ORDER BY c1.object_id) -1 
FROM 
    sys.columns c1 CROSS JOIN sys.columns c2 

SELECT 
    P.name, 
    DATEPART(year, DATEADD(month, N.Num, start_date)), 
    DATEPART(month, DATEADD(month, N.Num, start_date)), 
    P.paid/(DATEDIFF(month, start_date, end_date) + 1) 
FROM 
    #payment_details P 
    JOIN 
    #numbers N ON DATEDIFF(month, start_date, end_date) >= N.num 
+0

Very интересное решение, но как это работает? Когда я только не сделаю этого для клиентов с датой начала в витринах (start_date BETWEEN '20100301' AND '20100331'), результат, который я получаю, содержит месяцы 2010-01 и 2010-02, что неверно. Как я могу избежать этого? –

+0

Немного взлома с использованием Sys.Columns для создания таблицы Tally, но что это за блестящий взлом. Я буду использовать его для динамического создания списка дней между двумя датами (вместо добавления постоянной таблицы календаря) и посмотреть, как идет работа. Спасибо! – MikeTeeVee

0

Просто, чтобы предложить какое-то боковое мышление, вам нужно Нужна строка за каждый месяц? В зависимости от того, являетесь ли вы, скажем, вычисляете что-либо из этого или создаете отчет для кого-то для просмотра, вам может не понадобиться создавать 6 строк, где единственными разными данными являются месяц/год. Это также предполагает, что платежи распределяются равномерно по месяцам.Это можно было бы легко написать, выбрав имя, начало года, начало месяца, конец года, конец месяца, а затем разделив общую сумму, уплаченную ((разница в году * 12) + разница в месяц), чтобы определить ежемесячный платеж.

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

+0

Мне действительно нужна строка за каждый месяц. Это цель отчета, в котором этот запрос должен использоваться. платежи ежемесячно распределяются, так что это не проблема :-) –

Смежные вопросы