문제

보고서를 작성하는 데 필요한 월별 상태 데이터베이스 보기가 있습니다.뷰의 데이터는 다음과 같습니다.

Category | Revenue  |  Yearh  |  Month
Bikes      10 000      2008        1
Bikes      12 000      2008        2
Bikes      12 000      2008        3
Bikes      15 000      2008        1
Bikes      11 000      2007        2
Bikes      11 500      2007        3
Bikes      15 400      2007        4


...기타 등등

뷰에는 제품 범주, 수익, 연도 및 월이 있습니다.2007년과 2008년을 비교하여 판매가 없는 달을 0으로 표시하는 보고서를 만들고 싶습니다.따라서 보고서는 다음과 같아야 합니다.

Category  |  Month  |  Rev. This Year  |  Rev. Last Year
Bikes          1          10 000               0
Bikes          2          12 000               11 000
Bikes          3          12 000               11 500
Bikes          4          0                    15 400


주목해야 할 중요한 점은 월 1의 매출이 2008년에만 있고 따라서 2007년에는 0이라는 것입니다.또한 4번째 달에는 2008년에만 매출이 없었으므로 0이 되지만 2007년에는 매출이 있었고 여전히 표시됩니다.

또한 보고서는 실제로 회계 연도에 대한 것이므로 2007년이나 2008년 5월에 매출이 없었다면 둘 다 0인 빈 열을 갖고 싶습니다.

내가 얻은 쿼리는 다음과 같습니다.

SELECT 
    SP1.Program,
    SP1.Year,
    SP1.Month,
    SP1.TotalRevenue,
    IsNull(SP2.TotalRevenue, 0) AS LastYearTotalRevenue

FROM PVMonthlyStatusReport AS SP1 
     LEFT OUTER JOIN PVMonthlyStatusReport AS SP2 ON 
                SP1.Program = SP2.Program AND 
                SP2.Year = SP1.Year - 1 AND 
                SP1.Month = SP2.Month
WHERE 
    SP1.Program = 'Bikes' AND
    SP1.Category = @Category AND 
    (SP1.Year >= @FinancialYear AND SP1.Year <= @FinancialYear + 1) AND
    ((SP1.Year = @FinancialYear AND SP1.Month > 6) OR 
     (SP1.Year = @FinancialYear + 1 AND SP1.Month <= 6))

ORDER BY SP1.Year, SP1.Month

이 쿼리의 문제점은 위 예제 데이터의 네 번째 행을 반환하지 않는다는 것입니다. 2008년에는 매출이 없었지만 실제로는 2007년에 매출이 발생했기 때문입니다.

이것은 아마도 일반적인 쿼리/문제일 것입니다. 그러나 오랫동안 프런트 엔드 개발을 수행한 후 내 SQL이 녹슬었습니다.어떤 도움이라도 대단히 감사하겠습니다!

아, 그런데 저는 이 쿼리에 SQL 2005를 사용하고 있습니다. 도움이 될 만한 새로운 유용한 기능이 있으면 알려주시기 바랍니다.

도움이 되었습니까?

해결책

Case 진술은 나의 가장 친한 SQL 친구입니다.두 달 모두에 0 레브를 생성하려면 시간에 대한 표도 필요합니다.

가정은 다음 표의 가용성을 기반으로 합니다.

매상:카테고리 | 수익 | onh | 월

그리고

TM:해 | 달 (보고에 필요한 모든 날짜가 포함되어 있음)

빈 행이 없는 예 1:

select
    Category
    ,month
    ,SUM(CASE WHEN YEAR = 2008 THEN Revenue ELSE 0 END) this_year
    ,SUM(CASE WHEN YEAR = 2007 THEN Revenue ELSE 0 END) last_year

from
    sales

where
    year in (2008,2007)

group by
    Category
    ,month

보고:

Category  |  Month  |  Rev. This Year  |  Rev. Last Year
Bikes          1          10 000               0
Bikes          2          12 000               11 000
Bikes          3          12 000               11 500
Bikes          4          0                    15 400

빈 행이 있는 예 2:나는 하위 쿼리를 사용할 예정이며(다른 쿼리는 그렇지 않을 수도 있음) 모든 제품 및 연도 월 콤보에 대해 빈 행을 반환합니다.

select
    fill.Category
    ,fill.month
    ,SUM(CASE WHEN YEAR = 2008 THEN Revenue ELSE 0 END) this_year
    ,SUM(CASE WHEN YEAR = 2007 THEN Revenue ELSE 0 END) last_year

from
    sales
    Right join (select distinct  --try out left, right and cross joins to test results.
                   product
                   ,year
                   ,month
               from
                  sales --this ideally would be from a products table
                  cross join tm
               where
                    year in (2008,2007)) fill


where
    fill.year in (2008,2007)

group by
    fill.Category
    ,fill.month

보고:

Category  |  Month  |  Rev. This Year  |  Rev. Last Year
Bikes          1          10 000               0
Bikes          2          12 000               11 000
Bikes          3          12 000               11 500
Bikes          4          0                    15 400
Bikes          5          0                    0
Bikes          6          0                    0
Bikes          7          0                    0
Bikes          8          0                    0

대부분의 보고 도구는 이 크로스탭 또는 매트릭스 기능을 수행하며 이제 SQL Server 2005에는 이 작업도 수행하는 피벗 구문이 있다고 생각합니다.

다음은 몇 가지 추가 리소스입니다.사례http://www.4guysfromrolla.com/webtech/102704-1.shtmlSQL 서버 2005 피벗http://msdn.microsoft.com/en-us/library/ms177410.aspx

다른 팁

@Christian -- 마크다운 편집기 -- UGH;특히 게시물의 미리보기와 최종 버전이 일치하지 않는 경우...@Christian - 완전 외부 조인 - WHERE 절에 SP1에 대한 참조가 있고 JOIN 뒤에 WHERE 절이 적용된다는 사실로 인해 완전 외부 조인이 무시됩니다.테이블 중 하나를 필터링하여 완전 외부 조인을 수행하려면 WHERE 절을 하위 쿼리에 넣어 필터링이 수행되도록 해야 합니다. ~ 전에 또는 모든 WHERE 기준을 JOIN ON 절에 구축하려고 시도합니다. 이는 엄청나게 추악합니다.사실 이 작업을 수행할 수 있는 좋은 방법은 없습니다.

@조나스:이것을 고려하면:

또한 보고서는 실제로 회계연도에 대한 것입니다. 2007년이나 2008년 5월에 판매가 없었다면 둘 다 0인 빈 열을 갖고 싶습니다.

그리고 이 작업은 예쁜 쿼리로는 수행할 수 없다는 사실을 고려하여 실제로 원하는 결과를 얻으려고 노력할 것입니다.추악한 쿼리를 사용하거나 실제로 원하는 정확한 데이터를 얻지 못하는 것은 의미가 없습니다.;)

따라서 저는 이 작업을 5단계로 수행할 것을 제안합니다.
1.결과를 일치시키려는 형식으로 임시 테이블을 만듭니다.
2.월 열에 1-12를 포함하여 12개의 행으로 채웁니다.
삼.SP1 로직을 사용하여 "올해" 열을 업데이트하세요.
4.SP2 로직을 사용하여 "작년" 열을 업데이트하세요.
5.임시 테이블에서 선택

물론 이 작업을 수행하기 위해 저장 프로시저를 만들 수 있다는 가정하에 작업하고 있는 것 같습니다.기술적으로는 이 전체 배치를 인라인으로 실행할 수 있지만 그런 종류의 추악함은 거의 볼 수 없습니다.SP를 만들 수 없는 경우 하위 쿼리를 통해 전체 외부 조인으로 돌아가는 것이 좋습니다. 하지만 한 달에 어느 해에도 매출이 없으면 행을 얻을 수 없습니다.

가격 인하 정보 - 네, 답답하네요.편집자가 내 HTML 테이블을 미리 보았지만 게시한 후에는 사라졌습니다. 따라서 게시물에서 모든 HTML 형식을 제거해야 했습니다...

@kcrumley 비슷한 결론에 도달한 것 같습니다.이 쿼리는 쉽게 정말 추악해집니다.나는 실제로 귀하의 답변을 읽기 전에 유사하지만 다른 접근 방식을 사용하여 이 문제를 해결했습니다.보고 데이터베이스에 저장 프로시저와 함수를 생성할 수 있는 액세스 권한이 있습니다.제품 범주와 회계 연도를 매개 변수로 사용하는 테이블 값 함수를 만들었습니다.이를 기반으로 함수는 12개의 행을 포함하는 테이블을 채웁니다.판매가 가능한 경우 행은 뷰의 데이터로 채워지고, 그렇지 않은 경우 행의 값은 0이 됩니다.

그런 다음 함수에서 반환된 두 테이블을 조인합니다.모든 테이블에 12개의 로브가 있다는 것을 알고 있으므로 할당이 더 쉽고 제품 카테고리 및 월에 참여할 수 있습니다.

SELECT 
    SP1.Program,
    SP1.Year,
    SP1.Month,
    SP1.TotalRevenue AS ThisYearRevenue,
    SP2.TotalRevenue AS LastYearRevenue
FROM GetFinancialYear(@Category, 'First Look',  2008) AS SP1 
     RIGHT JOIN GetFinancialYear(@Category, 'First Look',  2007) AS SP2 ON 
         SP1.Program = SP2.Program AND 
         SP1.Month = SP2.Month

GetFinancialYear 함수가 꽤 지저분하기 때문에 접근 방식이 좀 더 깔끔할 것 같습니다!그러나 적어도 그것은 효과가 있습니다. 지금은 그것이 저를 행복하게 만듭니다 ;)

비결은 ISNULL을 사용하여 FULL JOIN을 수행하여 두 테이블 중 하나에서 조인된 열을 가져오는 것입니다.저는 보통 이것을 뷰나 파생 테이블에 래핑합니다. 그렇지 않으면 WHERE 절에서도 ISNULL을 사용해야 합니다.

SELECT 
    Program,
    Month,
    ThisYearTotalRevenue,
    PriorYearTotalRevenue
FROM (
    SELECT 
        ISNULL(ThisYear.Program, PriorYear.Program) as Program,
        ISNULL(ThisYear.Month, PriorYear.Month),
        ISNULL(ThisYear.TotalRevenue, 0) as ThisYearTotalRevenue,
        ISNULL(PriorYear.TotalRevenue, 0) as PriorYearTotalRevenue
    FROM (
        SELECT Program, Month, SUM(TotalRevenue) as TotalRevenue 
        FROM PVMonthlyStatusReport 
        WHERE Year = @FinancialYear 
        GROUP BY Program, Month
    ) as ThisYear 
    FULL OUTER JOIN (
        SELECT Program, Month, SUM(TotalRevenue) as TotalRevenue 
        FROM PVMonthlyStatusReport 
        WHERE Year = (@FinancialYear - 1) 
        GROUP BY Program, Month
    ) as PriorYear ON
        ThisYear.Program = PriorYear.Program
        AND ThisYear.Month = PriorYear.Month
) as Revenue
WHERE 
    Program = 'Bikes'
ORDER BY 
    Month

그러면 최소 요구 사항(2007년이나 2008년 또는 둘 다의 매출이 있는 행)이 제공됩니다.어느 연도에도 매출이 없는 행을 얻으려면 1-12 숫자 테이블에 INNER JOIN을 수행하면 됩니다. 그 중 하나를 가지고, 그렇지 않습니까?).

제가 틀렸을 수도 있지만 Left 조인 대신 완전 외부 조인을 사용해야 하지 않나요?이렇게 하면 두 테이블 모두에서 '빈' 열을 얻게 됩니다.

http://en.wikipedia.org/wiki/Join_(SQL)#Full_outer_join

피벗과 Dynamic Sql을 사용하면 이 결과를 얻을 수 있습니다.

SET NOCOUNT ON
IF OBJECT_ID('TEMPDB..#TEMP') IS NOT NULL
DROP TABLE #TEMP

;With cte(Category , Revenue  ,  Yearh  ,  [Month])
AS
(
SELECT 'Bikes', 10000, 2008,1 UNION ALL
SELECT 'Bikes', 12000, 2008,2 UNION ALL
SELECT 'Bikes', 12000, 2008,3 UNION ALL
SELECT 'Bikes', 15000, 2008,1 UNION ALL
SELECT 'Bikes', 11000, 2007,2 UNION ALL
SELECT 'Bikes', 11500, 2007,3 UNION ALL
SELECT 'Bikes', 15400, 2007,4
)
SELECT * INTO #Temp FROM cte

Declare @Column nvarchar(max),
        @Column2 nvarchar(max),
        @Sql nvarchar(max)


SELECT @Column=STUFF((SELECT DISTINCT ','+ 'ISNULL('+QUOTENAME(CAST(Yearh AS VArchar(10)))+','+'''0'''+')'+ 'AS '+ QUOTENAME(CAST(Yearh AS VArchar(10)))
FROM #Temp order by 1 desc FOR XML PATH ('')),1,1,'')

SELECT @Column2=STUFF((SELECT DISTINCT ','+ QUOTENAME(CAST(Yearh AS VArchar(10)))
FROM #Temp FOR XML PATH ('')),1,1,'')

SET @Sql= N'SELECT Category,[Month],'+ @Column +'FRom #Temp
            PIVOT
            (MIN(Revenue) FOR yearh IN ('+@Column2+')
            ) AS Pvt

            '
EXEC(@Sql)
Print @Sql

결과

Category    Month   2008    2007
----------------------------------
Bikes       1       10000   0
Bikes       2       12000   11000
Bikes       3       12000   11500
Bikes       4       0       15400
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top