-2

I have a table that looks like the following which shows the count of types. I need to and have been trying to display data as 1 column and 7 rows instead though... without success.

__________________________________________________________________________
| col types      | win2k | winxp | win2k3 | vista | win7 | win8 | win8.1 |
--------------------------------------------------------------------------
| count of types |  2365 | 65655 | 422445 | 4822  | 482  | 2331 | 485323 |
--------------------------------------------------------------------------
Select 
count(case when col1 ~* '5.0.2195' then 1 else null end) as Win2k,
count(case when col1 ~* '5.1.2600' then 1 else null end) as WinXP, 
count(case when col1 ~* '5.2.3790' then 1 else null end) as W2k3,
count(case when (col1 ~* '6.0.6000'
    or col1 ~* '6.0.6001' or col1 ~* '6.0.6002') 
    then 1 else null end) as Vista,
count(case when (col1 ~* '6.1.7600'
    or col1 ~* '6.1.7601')
    then 1 else null end) as Win7,
count(case when col1 ~* '6.2.9200' then 1 else null end) as Win8,
count(case when (col1 ~* '6.3.9200'
    or col1 ~* '6.3.9600')
    then 1 else null end) as "Win8.1"
From col1

Ideally it would look something like this:

___________________
| types  | count  |
-------------------
| win2k  | 2365   |
| winxp  | 65655  |
| win2k3 | 422445 | 
| vista  | 4822   |
| win7   | 482    |
| win8   | 2331   |
| win8.1 | 485323 |
-------------------

Notes:

  • I am using Postgresql 9.3 with PGADMIN III
  • I cannot create any custom functions
  • It wouldn't matter if there were more columns to make this work
gunr2171
  • 16,104
  • 25
  • 61
  • 88
  • 2
    possible duplicate of [unpivot and PostgreSQL](http://stackoverflow.com/questions/1128737/unpivot-and-postgresql) – Bulat Sep 11 '14 at 22:27
  • I think Bulat is correct, you want a (un)pivot table which is the opposite of http://www.postgresql.org/docs/9.1/static/tablefunc.html – mjallday Sep 11 '14 at 22:48
  • Bulat, I looked and tried that reference before I posted and although it may seem similar, it has a different table/data structure that doesn't apply. My singular row is aggregated data making the unpivot very difficult if not impossible. – BooneStars Sep 11 '14 at 23:50
  • Of course it's possible. Your presented query is illegal and confusing, so it's hard to tell, what's best for you. `profile.foo` is not a legal column name in the context of your query. Pleae repair the question. You could provide the table definition and some sample values to clarify. – Erwin Brandstetter Sep 11 '14 at 23:56
  • First question posted... still learning. – BooneStars Sep 12 '14 at 01:45
  • The question is misleading, @cha got into your mind. – Bulat Sep 12 '14 at 07:47

3 Answers3

0

I like to use the Postgres specific parallel unnest() for this:

SELECT unnest('{win2k,winxp,win2k3,vista,win7,win8,win8.1}'::text[]) AS type
      ,unnest(ARRAY[
          count(some_column ~ '5.0.2195' OR NULL)
         ,count(some_column ~ '5.1.2600' OR NULL)
          .. the rest from your query above ...
       ]) AS ct
FROM   profile.foo

Sequence and number of values in both arrays must match.
Related answers with more details (be sure to read up!):

Alternative counting technique (optional)

count() only counts non-null values..

(TRUE  OR NULL) IS TRUE  
(FALSE OR NULL) IS NULL  
(NULL  OR NULL) IS NULL

Voilá. Only TRUE is counted. More details in this answer on dba.SE.

Aside: Using ~ instead of ~* in the expressions, since there are no case sensitive letters in these string literals. But I doubt you need a regular expression match at all.

Also, the column names profile.foo don't make sense, since the only table is named foo, not profile

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

These type of queries are easier to make with an aim of GROUP BY, like this:

Select 
case when profile.foo ~* '5.0.2195' then 'Win2k'
     when profile.foo ~* '5.1.2600' then 'WinXP' 
     when profile.foo ~* '5.2.3790' then 'W2k3'
     when (profile.foo ~* '6.0.6000'
        or profile.foo ~* '6.0.6001'
        or profile.foo ~* '6.0.6002') 
        then 'Vista'
     when (profile.foo ~* '6.1.7600'
        or profile.foo ~* '6.1.7601')
        then 'Win7'
     when profile.foo ~* '6.2.9200' then 'Win8'
     when (profile.foo ~* '6.3.9200'
        or profile.foo ~* '6.3.9600')
        then 'Win8.1' ELSE 'Other' END as type,
     count(*) as cnt
From profile
GROUP BY 1

As commented below this query will work for mutually exclusive cases, i.e. when profile.foo contains a value representing one OS per row

cha
  • 10,301
  • 1
  • 18
  • 26
  • In the from statement, I removed the column name and it worked perfect. Thanks! – BooneStars Sep 11 '14 at 23:51
  • As commented on the other (equivalent and earlier) question, this answer is only valid for mutually exclusive cases. Simplify with `GROUP BY 1`. – Erwin Brandstetter Sep 12 '14 at 00:09
  • @ErwinBrandstetter: thanks for the comment. I have made an edit that reflects your comment. BTW, if there are multiple values in the `profile.foo` the counts can be performed using a `UNION ALL` – cha Sep 12 '14 at 00:14
  • `UNION ALL`, yes, but much more expensive due to multiple sequential scans. – Erwin Brandstetter Sep 12 '14 at 00:15
0

Instead of using conditional aggregates, just use CASE to populate the Type appropriately then group on Type:

   ;with cte AS (Select   case when profile.foo ~* '5.0.2195' then 'Win2k'
                               when profile.foo ~* '5.1.2600' then 'WinXP' 
                               when profile.foo ~* '5.2.3790' then 'W2k3'
                               when profile.foo ~* '6.0.6000' or profile.foo ~* '6.0.6001' or profile.foo ~* '6.0.6002' then 'Vista'
                               when (profile.foo ~* '6.1.7600' or profile.foo ~* '6.1.7601') then 'Win7'
                               when profile.foo ~* '6.2.9200' then 'Win8'
                               when (profile.foo ~* '6.3.9200' or profile.foo ~* '6.3.9600') then 'Win8.1'
                          end as Type
                 From profile.foo)
    SELECT Type,COUNT(*) AS ct
    FROM cte
    GROUP BY Type

Not 100% on postgresql syntax, but the logic is compatible.

Hart CO
  • 34,064
  • 6
  • 48
  • 63
  • A `CASE` statement produces exactly 1 match per row, while in the original query each row could match multiple expressions. (Illegal column name in the Q aside.) Probably not intended by the author, but still potentially distinct results. – Erwin Brandstetter Sep 11 '14 at 22:48
  • I ran this and got: ERROR: schema "profile" does not exist LINE 9: From profile.flavor) ^ ********** Error ********** ERROR: schema "profile" does not exist SQL state: 3F000 Character: 914 – BooneStars Sep 11 '14 at 22:57
  • @ErwinBrandstetter None of the criteria overlaps between `CASE` statements in the original query, so counts will be equivalent to what I've done here, just as rows instead of columns, barring syntax issues, of course. – Hart CO Sep 11 '14 at 23:22
  • @BooneStars I don't know offhand what's causing that error, I'm not very familiar with postgresql syntax. – Hart CO Sep 11 '14 at 23:24
  • This is a logic error. Conditions don't have to overlap. A single string can match any number regular expressions, especially since they are not anchored. Consider the value '5.0.2195 5.1.2600'. I am dropping this now. There is too much floating around that does not add up. – Erwin Brandstetter Sep 11 '14 at 23:59
  • Not referring to the syntax error, which is the OP's doing. (Illegal table or column name.) – Erwin Brandstetter Sep 12 '14 at 00:17