3

I have 3 tables. Team, Option, OptionTeam.
The Team holds a TeamId and Name
Option holds OptionId, OptionGroup
OptionTeam holds TeamId, OptionId, OptionGroup

select a.TeamId, a.Name
(select count(*) from OptionTeam ot where ot.TeamId=a.TeamId and ot.OptionGroup=4) as Option1,
(select count(*) from OptionTeam ot where ot.TeamId=a.TeamId and ot.OptionGroup=5) as Option2,
(select count(*) from OptionTeam ot where ot.TeamId=a.TeamId and ot.OptionGroup=6) as Option3,
(select count(*) from OptionTeam ot where ot.TeamId=a.TeamId and ot.OptionGroup=11) as Option4
from Team a 

I want to get a list of Teams, and extra columns indicating how many options of each group are connected to each Team. This is done by the above query, but I want to replace the 4,5,6,11 with values of OptionGroup from a table Option.
It has to be dynamic, because there might be a new OptionGroup in the future, and I want the stored procedure to be able to handle it.

Sample data:

Team  
TeamId  
1  
2  
3  

Option

OptionId | OptionGroup  
11 | 4  
12 | 5  
13 | 4  
14 | 4  
15 | 5  

OptionTeam

TeamId | OptionId | OptionGroup  
1 | 11 | 4  
1 | 13 | 4  
2 | 12 | 5  
2 | 14 | 4  
3 | 15 | 5  

And the list I want to get is

TeamId | Group4 (OptionGroup=4) | Group5 (OptionGroup=5)  
1 | 2 | 0  
2 | 1 | 1  
3 | 0 | 1  
1
  • 1
    What RDBMS are you using? MySQL, SQL Server, etc? Commented Nov 1, 2010 at 14:15

3 Answers 3

5
+50

You'll need a dynamic pivot to do this. Here's the stored procedure:

CREATE PROC [dbo].[pivotsp]
      @query    AS NVARCHAR(MAX),                   -- The query, can also be the name of a table/view.
      @on_rows  AS NVARCHAR(MAX),                   -- The columns that will be regular rows.
      @on_cols  AS NVARCHAR(MAX),                   -- The columns that are to be pivoted.
      @agg_func AS NVARCHAR(257) = N'SUM',          -- Aggregate function.
      @agg_col  AS NVARCHAR(MAX),                   -- Column to aggregate.
      @output   AS NVARCHAR(257) = N'',             -- Table for results
      @debug    AS bit = 0                          -- 1 for debugging
    AS

    -- Example usage:
    --    exec pivotsp
    --          'select * from vsaleshistory',
    --          'market,marketid,family,familyid,Forecaster,Forecasterid,product,productid',
    --          'month',
    --          'sum',
    --          'ku',
    --          '##sales'

    -- Input validation
    IF @query IS NULL OR @on_rows IS NULL OR @on_cols IS NULL
       OR @agg_func IS NULL OR @agg_col IS NULL
    BEGIN
      RAISERROR('Invalid input parameters.', 16, 1);
      RETURN;
    END

    -- Additional input validation goes here (SQL Injection attempts, etc.)

    BEGIN TRY
      DECLARE
        @sql     AS NVARCHAR(MAX),
        @cols    AS NVARCHAR(MAX),
        @newline AS NVARCHAR(2);

      SET @newline = NCHAR(13) + NCHAR(10);

      -- If input is a valid table or view
      -- construct a SELECT statement against it
      IF COALESCE(OBJECT_ID(@query, N'U'),
                  OBJECT_ID(@query, N'V')) IS NOT NULL
        SET @query = N'SELECT * FROM ' + @query;

      -- Make the query a derived table
      SET @query = N'(' + @query + N') AS Query';

      -- Handle * input in @agg_col
      IF @agg_col = N'*'
        SET @agg_col = N'1';

      -- Construct column list
      SET @sql =
          N'SET @result = '                                    + @newline +
          N'  STUFF('                                          + @newline +
          N'    (SELECT N'','' +  quotename( '
                       + 'CAST(pivot_col AS sysname)' +
                       + ')  AS [text()]'                          + @newline +
          N'     FROM (SELECT DISTINCT('
                       + @on_cols + N') AS pivot_col'              + @newline +
          N'           FROM' + @query + N') AS DistinctCols'   + @newline +
          N'     ORDER BY pivot_col'                           + @newline +
          N'     FOR XML PATH(''''))'                          + @newline +
          N'    ,1, 1, N'''');'

      IF @debug = 1
         PRINT @sql

      EXEC sp_executesql
        @stmt   = @sql,
        @params = N'@result AS NVARCHAR(MAX) OUTPUT',
        @result = @cols OUTPUT;

      IF @debug = 1
         PRINT @cols

      -- Create the PIVOT query
      IF @output = N''
          begin
            SET @sql =
                N'SELECT *'                                          + @newline +
                N'FROM (SELECT '
                              + @on_rows
                              + N', ' + @on_cols + N' AS pivot_col'
                              + N', ' + @agg_col + N' AS agg_col'        + @newline +
                N'      FROM ' + @query + N')' +
                              + N' AS PivotInput'                        + @newline +
                N'  PIVOT(' + @agg_func + N'(agg_col)'               + @newline +
                N'    FOR pivot_col IN(' + @cols + N')) AS PivotOutput;'
          end
      ELSE
          begin
            set @sql = 'IF  EXISTS (SELECT * FROM tempdb.sys.objects WHERE  ' +
                'name = ''' + @output + ''' AND type = N''U'') DROP TABLE tempdb.' + @output
            EXEC sp_executesql @sql;

            SET @sql =
                N'SELECT * INTO ' + @output                          + @newline +
                N'FROM (SELECT '
                              + @on_rows
                              + N', ' + @on_cols + N' AS pivot_col'
                              + N', ' + @agg_col + N' AS agg_col'        + @newline +
                N'      FROM ' + @query + N')' +
                              + N' AS PivotInput'                        + @newline +
                N'  PIVOT(' + @agg_func + N'(agg_col)'               + @newline +
                N'    FOR pivot_col IN(' + @cols + N')) AS PivotOutput;'
          end

        IF @debug = 1
           PRINT @sql

        EXEC sp_executesql @sql;
    END TRY
    BEGIN CATCH
      DECLARE
        @error_message  AS NVARCHAR(2047),
        @error_severity AS INT,
        @error_state    AS INT;

      SET @error_message  = ERROR_MESSAGE();
      SET @error_severity = ERROR_SEVERITY();
      SET @error_state    = ERROR_STATE();

      RAISERROR(@error_message, @error_severity, @error_state);

      RETURN;
    END CATCH

With that, it's easy to pivot on a variable number of columns:

EXEC pivotsp
        'SELECT TeamID, OptionGroup, OptionID AS Options FROM OptionTeam',
        'Teamid',        -- Row headers
        'optiongroup',   -- item to aggregate
        'count',         -- aggregation function
        'optiongroup',   -- Column header
        '##temp'         -- output table name
    SELECT * FROM ##temp

Results:

   Teamid   4   5
    1   2   0
    2   1   1
    3   0   1
Sign up to request clarification or add additional context in comments.

Comments

2
SELECT  a.*, o.optionGroup, COUNT(*)
FROM    team a
CROSS JOIN
        option o
JOIN    OptionTeam ot
ON      ot.teamId = a.teamId
        AND ot.optionGroup = o.optionGroup
WHERE   o.OptionId = @id
GROUP BY
        a.teamId, o.optionGroup

5 Comments

Where do you get the @id from?
@Stavros: that's what I should ask you :) Please post the sample data of your tables and the output you'd like to get.
@Quassnoi: I have just added the sample data :)
@Stavros: so if there is a third optiongroup, you want a third column for it?
@Stavros: you can't do it with SQL, unless you make a dynamic query (a query composed in runtime).
0
select teamID,
sum(case when optionGroup = 4 then 1 else 0 end) as optionGroup4,
sum(case when optionGroup = 5 then 1 else 0 end) as optionGroup5,
from optionteam
group by teamID

to add more optiongroups without changing the code, try grouping by that field:

select teamID,optionGroup,count(optionID) as optionCount
from optionteam
group by teamID,optionGroup

1 Comment

This doesn't create different columns :(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.