question

sqlnative avatar image
sqlnative asked

Counting at row level

There are an interesting query problem I am facing. I am trying solve using single query, opposite to use of temporary tables or string concatenation functions.

Basically, I have Table “T” with an integer id and 6 populated columns A, B, C, D, E, F with integer values. I have to populate columns A1, A2, A3, A4, A5, A6 where:

A1 value should be populated with the number of occurrences of value in the field A over fields A, B, C, D, E, F in the same row

B1 value should be populated with the number of occurrences of value in the field B over fields A, B, C, D, E, F in the same row

Etc…

Sample of values with calculated values for A1, A2, A3, A4, A5, A6 after running a query should be:

t-sqlquery
sample.png (9.6 KiB)
1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

I know that using any other language that support matrix calculation will make the solution more elegant. However, performing calculation using other language and than using SQL to perform UPDATE is mixture that adds more complexity. Solving it using SQL only should be more elegant solution taking in consideration that calculation is based on simple COUNT operation.

0 Likes 0 ·
Oleg avatar image
Oleg answered

This problem is actually pretty simple, except that the question description is somewhat confusing. There is a request to populate columns A1, A2, A3, A4, A5, A6, while the image displays columns named A1, B1, C1, D1, E1 and F1. The explanation of the requirement mentions columns A1, B1, etc. It would be best to ask the question accurately enough as not to cause any ambiguity in what actually needs to be done.

Assuming that there are no NULL values in any of the columns, one possible solution is to simply compare every column value to the value in the given column and sum up if the values are the same. Since the value in any of the columns is always going to be equal to itself, the smallest resulting value in columns A1, B1, C1, D1, E1 and F1 (I picked this one from the available combinations) is going to be 1 while the maximum value is 6 (because there are 6 columns). Here is the script which will work as expected:

select
    A, B, C, D, E, F,

    1 + case when A = B then 1 else 0 end + 
    case when A = C then 1 else 0 end + case when A = D then 1 else 0 end + 
    case when A = E then 1 else 0 end + case when A = F then 1 else 0 end A1,

    case when B = A then 1 else 0 end + 1 + 
    case when B = C then 1 else 0 end + case when B = D then 1 else 0 end + 
    case when B = E then 1 else 0 end + case when B = F then 1 else 0 end B1,

    case when C = A then 1 else 0 end + case when C = B then 1 else 0 end + 
    1 + case when C = D then 1 else 0 end + 
    case when C = E then 1 else 0 end + case when C = F then 1 else 0 end C1,

    case when D = A then 1 else 0 end + case when D = B then 1 else 0 end + 
    case when D = C then 1 else 0 end + 1 + 
    case when D = E then 1 else 0 end + case when D = F then 1 else 0 end D1,

    case when E = A then 1 else 0 end + case when E = B then 1 else 0 end + 
    case when E = C then 1 else 0 end + case when E = D then 1 else 0 end + 
    1 + case when E = F then 1 else 0 end E1,

    case when F = A then 1 else 0 end + case when F = B then 1 else 0 end + 
    case when F = C then 1 else 0 end + case when F = D then 1 else 0 end + 
    case when F = E then 1 else 0 end + 1 F1

    from YourTable

Using the sample data in question, the query produces the following output:

A    B    C    D    E    F    A1   B1   C1   D1   E1   F1
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
0    0    2    2    3    3    2    2    2    2    2    2
0    2    2    2    3    1    1    3    3    3    1    1
3    4    1    3    1    2    2    1    2    2    2    1
3    0    0    1    1    3    2    2    2    2    2    2
0    4    1    2    3    3    1    1    1    1    2    2

Hope this helps.

Oleg

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Jeff Moden avatar image
Jeff Moden answered

@sqlnative

First of all, the graphic is great for showing what you want to end up with but... it would also be incredibly helpful if you posted "Readily Consumable" data in the future to help those trying to help you, especially those of us that like to test our solutions before we post them.

Here's one way you could have done that for this post.

  SELECT IntID = IDENTITY(INT,1,1)
        ,A,B,C,D,E,F
   INTO #TestTable
   FROM (VALUES 
         (0,0,2,2,3,3)
        ,(0,2,2,2,3,1)
        ,(3,4,1,3,1,2)
        ,(3,0,0,1,1,3)
        ,(0,4,1,2,3,3)
        )v(A,B,C,D,E,F)
;

In anticipation of making it a bit easier to add or remove column in the future, here's a bit of code that is pretty easy to do so with.

   WITH CTE AS
(--==== Unpivot and count
 SELECT *
        ,RowValCnt = COUNT(*) OVER (PARTITION BY IntID,Val)
   FROM (SELECT IntID,A,B,C,D,E,F FROM #TestTable) t  
        UNPIVOT (Val FOR Col IN (A,B,C,D,E,F))unpvt
)--==== Repivot using a CROSSTAB, which simply displays the pivoted answer.
 SELECT IntID
        ,A  = MAX(CASE WHEN Col = 'A' THEN Val END)
        ,B  = MAX(CASE WHEN Col = 'B' THEN Val END)
        ,C  = MAX(CASE WHEN Col = 'C' THEN Val END)
        ,D  = MAX(CASE WHEN Col = 'D' THEN Val END)
        ,E  = MAX(CASE WHEN Col = 'E' THEN Val END)
        ,F  = MAX(CASE WHEN Col = 'F' THEN Val END)
        ,A1 = MAX(CASE WHEN Col = 'A' THEN RowValCnt END)
        ,B1 = MAX(CASE WHEN Col = 'B' THEN RowValCnt END)
        ,C1 = MAX(CASE WHEN Col = 'C' THEN RowValCnt END)
        ,D1 = MAX(CASE WHEN Col = 'D' THEN RowValCnt END)
        ,E1 = MAX(CASE WHEN Col = 'E' THEN RowValCnt END)
        ,F1 = MAX(CASE WHEN Col = 'F' THEN RowValCnt END)
   FROM CTE
  GROUP BY IntID
;

Here are the results...


zkx4g.png (5.5 KiB)
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.