Help with SQL Query

  • Hi all!,

    First post, so I hope I posted it in the right section...

    The query below is working fine, but I started to think if this really was the most efficient way to do it?

    Every "Org" can only appear once per Year and Month

    Select

    SUM(ISNULL(R1.Users,0))As 'Col1',

    SUM(ISNULL(R2.Users,0))As 'Col2',

    SUM(ISNULL(R3.Users,0))As 'Col3'

    From Users U

    Left Join (Select * from Report) R1 On U.UserAlias = R1.UserAlias AND

    U.Organization = 'Org1' AND

    R1.Year = 2008 And R1.Month = 4

    Left Join (Select * from Report) R2 On U.UserAlias = R2.UserAlias AND

    U.Organization = 'Org2' AND

    R2.Year = 2008 And R2.Month = 4

    Left Join (Select * from Report) R3 On U.UserAlias = R3.UserAlias AND

    U.Organization = 'Org3' AND

    R3.Year = 2008 And R3.Month = 4

    Example of wanted output:

    Col1 Col2 Col3

    11 22 33

    Alternatives on better solutions are appreciated.

  • Since you're using SQL 2005, look up PIVOT in books online.

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • Can you please post your DDL and some sample data?

    That will make it a lot easier to answer your question. 😉

  • Yes of course 🙂

    Sorry about that

    Here is a small example:

    CREATE TABLE #Users([UserAlias] [nchar](10) COLLATE Latin1_General_CI_AI NOT NULL,

    [FName] [nchar](20) COLLATE Latin1_General_CI_AI NULL,

    [LName] [nchar](20) COLLATE Latin1_General_CI_AI NULL,

    [Organization] [nchar](20) COLLATE Latin1_General_CI_AI NULL)

    INSERT INTO #Users (UserAlias,FName,LName ,Organization) VALUES ('doej','John','Doe','Org1')

    INSERT INTO #Users (UserAlias,FName,LName ,Organization) VALUES ('clarkc','Clark','Jones','Org2')

    INSERT INTO #Users (UserAlias,FName,LName ,Organization) VALUES ('holmesk','Kate','Holmes','Org3')

    CREATE TABLE #Report([UserAlias] [nchar](10) COLLATE Latin1_General_CI_AI NOT NULL,

    [Year] [int] NOT NULL,[Month] [int] NOT NULL,[Users] [int] NULL)

    INSERT INTO #Report (UserAlias,[Year],[Month],[Users]) VALUES ('doej',2008,4,755)

    INSERT INTO #Report (UserAlias,[Year],[Month],[Users]) VALUES ('clarkc',2008,4,10)

    INSERT INTO #Report (UserAlias,[Year],[Month],[Users]) VALUES ('holmesk',2008,4,555)

    INSERT INTO #Report (UserAlias,[Year],[Month],[Users]) VALUES ('doej',2008,5,755)

    INSERT INTO #Report (UserAlias,[Year],[Month],[Users]) VALUES ('calkc',2008,5,10)

    Select

    SUM(ISNULL(R1.Users,0)) As 'Org1',

    SUM(ISNULL(R2.Users,0)) As 'Org2',

    SUM(ISNULL(R3.Users,0)) As 'Org3'

    From #Users U

    Left Join (Select * from #Report) R1 On U.UserAlias = R1.UserAlias AND

    U.Organization = 'Org1' AND

    R1.Year = 2008 And R1.Month = 4

    Left Join (Select * from #Report) R2 On U.UserAlias = R2.UserAlias AND

    U.Organization = 'Org2' AND

    R2.Year = 2008 And R2.Month = 4

    Left Join (Select * from #Report) R3 On U.UserAlias = R3.UserAlias AND

    U.Organization = 'Org3' AND

    R3.Year = 2008 And R3.Month = 4

    Drop table #Users

    Drop table #Report

    Hmm PIVOT you say.. Never tried that. Still thinking SQL 2000 I guess 😉

  • Try:

    SELECT [Year], [Month] ,[Org1],[Org2],[Org3]

    FROM

    (SELECT U.Organization, R.Users , [Year], [Month]

    FROM #Report R INNER JOIN #USERS U ON U.UserAlias = R.UserAlias) AS src

    PIVOT ( SUM (Users) FOR Organization IN ( [Org1],[Org2],[Org3])) AS Pivoted


    * Noel

  • How about this?

    ;WITH OrganizationUsers AS

    (

    SELECT U.Organization

    ,R.Users

    ,R.[Month]

    ,R.[Year]

    FROM #Users U

    JOIN #Report R ON U.UserAlias = R.UserAlias

    )

    SELECT [Year]

    , [Month]

    , [Org1]

    , [Org2]

    , [Org3]

    FROM OrganizationUsers

    PIVOT( SUM(Users) FOR Organization IN ([Org1], [Org2], [Org3])) AS OrganizationPivot

    GROUP BY [Org1]

    , [Org2]

    , [Org3]

    , [Year]

    , [Month]

  • Joe Celko (6/18/2008)


    You used singular table names but they don't seem to have only one row. You used reserved words for column names or vague useless names like "organization" for attribute. You split dates apart instead of keeping them atomic. Let's use "foobar" for the missing properties or root attributes on the data elements until you have time to get up to ISO-11179 Standards.

    Here is a cleaned up version of the original:

    SELECT

    SUM(ISNULL(R1.users, 0)) AS col1,

    SUM(ISNULL(R2.users, 0)) AS col2,

    SUM(ISNULL(R3.users, 0)) AS col3

    FROM Users AS U

    LEFT OUTER JOIN

    (SELECT * FROM Reports) AS R1

    ON U.user_alias = R1.user_alias

    AND U.organization_foobar = 'Org1'

    AND R1.foobar_year = 2008

    AND R1.foobar_month = 4

    LEFT OUTER JOIN

    (SELECT * FROM Reports) AS R2

    ON U.user_alias = R2.user_alias

    AND U.organization_foobar = 'Org2'

    AND R2.foobar_year = 2008

    AND R2.foobar_month = 4

    LEFT OUTER JOIN

    (SELECT * FROM Reports) AS R3

    ON U.user_alias = R3.user_alias

    AND U.organization_foobar = 'Org3'

    AND R3.foobar_year = 2008

    AND R3.foobar_month = 4 ;

    Let's assume that you correct the split date and some other things about the missing DDL, then you should be able to write this and have it run 1-3 orders of magnitude faster:

    SELECT SUM (CASE WHEN U.organization_foobar = 'Org1' THEN 1 ELSE 0 END) AS org1_cnt,

    SUM (CASE WHEN U.organization_foobar = 'Org2' THEN 1 ELSE 0 END) AS org2_cnt,

    SUM (CASE WHEN U.organization_foobar = 'Org3' THEN 1 ELSE 0 END) AS org3_cnt

    FROM Users AS U, Reports AS R

    WHERE R.foobar_date BETWEEN '2008-04-01' AND '2008-04-30'

    AND U.user_alias = R2.user_alias;

    Erm ...... Someone is preaching "standards" and is not using "ANSI" Joins ?


    * Noel

  • Actually, Joe used ANSI joins. The second query is a cross-join with two Where statements. That's standard.

    (Have to admit, I don't like the layout he used, but that's got nothing to do with published standards.)

    - Gus "GSquared", RSVP, OODA, MAP, NMVP, FAQ, SAT, SQL, DNA, RNA, UOI, IOU, AM, PM, AD, BC, BCE, USA, UN, CF, ROFL, LOL, ETC
    Property of The Thread

    "Nobody knows the age of the human race, but everyone agrees it's old enough to know better." - Anon

  • Question: What's with all of the Left Join (Select * from #Report) ?

    Is that any different than doing a join like this:

    Select

    SUM(ISNULL(R1.Users,0)) As 'Org1',

    SUM(ISNULL(R2.Users,0)) As 'Org2',

    SUM(ISNULL(R3.Users,0)) As 'Org3'

    From #Users U

    Left Join #Report R1 On U.UserAlias = R1.UserAlias AND

    U.Organization = 'Org1' AND

    R1.Year = 2008 And R1.Month = 4

    Left Join #Report R2 On U.UserAlias = R2.UserAlias AND

    U.Organization = 'Org2' AND

    R2.Year = 2008 And R2.Month = 4

    Left Join #Report R3 On U.UserAlias = R3.UserAlias AND

    U.Organization = 'Org3' AND

    R3.Year = 2008 And R3.Month = 4

  • The second query is a cross-join with two Where statements. That's standard.

    I beg to differ. A comma is NOT an ANSI join, CROSS JOIN would be!


    * Noel

  • GSquared (6/18/2008)


    Actually, Joe used ANSI joins. The second query is a cross-join with two Where statements. That's standard.

    (Have to admit, I don't like the layout he used, but that's got nothing to do with published standards.)

    I was under the impression that putting the join criteria in the WHERE clause is a violation of said ANSI standard. After all (assuming you fix the typo here):

    U.user_alias = R.user_alias;--<--R2 is an invalid alias

    would be a join criterion wouldn't it?

    ----------------------------------------------------------------------------------
    Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?

  • Matt Miller (6/18/2008)


    GSquared (6/18/2008)


    Actually, Joe used ANSI joins. The second query is a cross-join with two Where statements. That's standard.

    (Have to admit, I don't like the layout he used, but that's got nothing to do with published standards.)

    I was under the impression that putting the join criteria in the WHERE clause is a violation of said ANSI standard. After all (assuming you fix the typo here):

    U.user_alias = R.user_alias;--<--R2 is an invalid alias

    would be a join criterion wouldn't it?

    Totally agree!


    * Noel

  • Thank you all for your suggestions and tips!

    ggraber: Yes you are right, don't know why I used the select * :hehe:

    I think I will go for the PIVOT suggested by noeld which seems more efficient.

    And yes, maybe I need to take a closer look at the standards...

  • They are both ANSI joins. The commas are the Old (SQL 89) syntax, the "JOIN"'s are the New (SQL 92 and later) syntax. OK, New-er syntax.

    I much prefer the newer syntax, I think using the old syntax makes you look like an ossified brachiosaurus.

    And, yes Joe, I did read your books (well some of them, I have a lot of reading to do). They are among my very favorites. Thanks for writing them.

    [font="Times New Roman"]-- RBarryYoung[/font], [font="Times New Roman"] (302)375-0451[/font] blog: MovingSQL.com, Twitter: @RBarryYoung[font="Arial Black"]
    Proactive Performance Solutions, Inc.
    [/font]
    [font="Verdana"] "Performance is our middle name."[/font]

Viewing 14 posts - 1 through 13 (of 13 total)

You must be logged in to reply to this topic. Login to reply