Question

I have a table that currently is a long list of devices and information about when they were sold. I need to take the table which would look something like this:

Item   |  Time Sold
--------------------
  A        05/2010
  B        04/2010
  C        03/2010
  A        04/2010
  A        05/2010

And then have a table with the item as the first column, with the count of the dates being the column headers, like below:

Item   |   Count_03/2010   |  Count_04/2010  |  Count_05/2010
-------------------------------------------------------------
  A    |         0         |         1       |        2
  B    |         0         |         1       |        0
  C    |         1         |         0       |        0

Is there a simple way of doing this? I know in other languages there is a single command to do it, was wondering if SQL had one.

EDIT

My problem is that there is more than one table, and in some tables the months may be different than in other tables. Is there anyway to write a script that will apply to them all, by getting the variables listed and then using those to create the columns in the code? I could write one if I knew the months would always be the same, but since they are variable, is there a way of doing this.

Was it helpful?

Solution

SQL Server 2005 and up has something called pivot, but it is still not one command and you need to know the values in the Time sold column. You can use a dynamic pivot approach as demonstrated by Itzik Ben-Gan in his Inside Microsoft SQL Server 2008: T-SQL Querying book

Example

create table #test (Item char(1),  TimeSold varchar(20))

  insert #test values('A','05/2010')
  insert #test values('B','04/2010')
  insert #test values('C','03/2010')
  insert #test values('A','04/2010')
  insert #test values('A','05/2010')

  SELECT *
FROM
(SELECT Item,TimeSold
FROM #test) AS pivTemp
PIVOT
(   COUNT(TimeSold)
    FOR TimeSold IN ([05/2010],[04/2010],[03/2010])
) AS pivTable

OTHER TIPS

I didn't test it, but the following should work. I uses subqueries to get the desired results:

select item, 
(select count(*) from items i where time_sold between '02/2010' AND '03/2010' i.item=item ),
(select count(*) from items i where time_sold between '03/2010' AND '04/2010' i.item=item ),
(select count(*) from items i where time_sold between '04/2010' AND '05/2010' i.item=item )
from items;

Natively MS-SQL does not provide any functionality to display data in the tabular format you want without knowing the values you want to generate the column names from.

There is a great article on dynamically creating crosstab queries in MS-SQL. It's a hack, but looks like it should work.

Otherwise a more flexible approach is to do...

Select Distinct
Item,
Time Sold,
Count([Item])
From
MyTable
Group By
Item,
Time Sold

This will give you:

    Item | Time Sold | Count
    ------------------------
    A    | 05/2010   | 2
    A    | 04/2010   | 1
    B    | 04/2010   | 1
    C    | 03/2010   | 1

This is a much better format to work with for data analysis, as you can write a query to handle the data as the column names are known.

For example, you can use this data in a reporting tool to:

  • Count all of the sold items
  • Show a distinct list of dates
  • Show a list of which dates had the most items sold

To get it into the report format you have requested, you're better off using a reporting tool such as Crystal Reports or Excel even.

Both Excel and Crystal Report support Pivot tables for displaying your data in the tabular format you request (and it'll look a lot nicer too!).

You can group by to get aggregated results per item. Using sum and case, you can count the number of items in a specific period. For example:

select  item
,       sum(case when time_sold between '02/2010' and '03/2010' then 1 end)
,       sum(case when time_sold between '03/2010' and '04/2010' then 1 end)
,       sum(case when time_sold between '04/2010' and '05/2010' then 1 end)
from    items
group by
        item

If you have source tables in multiple formats, union them together in a subquery. I'll limit this example to only one range to save space:

select  item
,       sum(IsInRange1)
from    (
        select  item
        ,       case when time_sold between '01/2010' and '03/2010' 
                          then 1 end as IsInRange1
        from    usa_items
        union all
        select  item
        ,       case when time_sold in ('gennaio', 'febbraio', 'marzo') 
                          then 1 end
        from    italian_items
        union all
        select  item
        ,       case when time_sold between '2010-01-01' and '2010-03-01' 
                          then 1 end
        from    iso_items
        ) SubqueryAlias
group by
        item
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top