Need help with a script to identify count of negative and positive numbers in a record

  • Hello All,

    I need help with a tsql query. Below is the table structure.

    CREATE TABLE [dbo].[TableName](

    [col1] [int] NOT NULL,

    [col2] [int] NOT NULL,

    [col3] [int] NOT NULL,

    [col4] [int] NOT NULL,

    [col5] [int] NOT NULL

    ) ON [PRIMARY]

    GO

    Select 1op 10* from tablename

    col1 col2 col3 col4 col5

    -151295-7

    -35-156-16

    10-14817-16

    -11-1052-4

    -6-27-138

    -42-6719

    -108716-14

    17-17-91418

    7191512-13

    4-7-12-13-11

    I need to get a count of negative numbers and positive in every row on a row by row basis. Thanks for your help in advance.

    “If your actions inspire others to dream more, learn more, do more and become more, you are a leader.” -- John Quincy Adams

  • SELECT

    CASE WHEN col1 < 0 THEN 1 ELSE 0 END + CASE WHEN col2 < 0 THEN 1 ELSE 0 END + CASE WHEN col3 < 0 THEN 1 ELSE 0 END + CASE WHEN col4 < 0 THEN 1 ELSE 0 END + CASE WHEN col5 < 0 THEN 1 ELSE 0 END AS CountNegs,

    CASE WHEN col1 >= 0 THEN 1 ELSE 0 END + CASE WHEN col2 >= 0 THEN 1 ELSE 0 END + CASE WHEN col3 >= 0 THEN 1 ELSE 0 END + CASE WHEN col4 >= 0 THEN 1 ELSE 0 END + CASE WHEN col5 >= 0 THEN 1 ELSE 0 END AS CountPos

    FROM TableName

  • Thanks for your help. It works like a charm

    “If your actions inspire others to dream more, learn more, do more and become more, you are a leader.” -- John Quincy Adams

  • There is another, more mathematical way:

    If no zero values in your columns:

    select *, 5-Positives as Negatives

    from (select *,(sign(col1)+sign(col2)+sign(col3)+sign(col4)+sign(col5)+5)/2 Positives

    from tablename) sn

    And, if there are zero values:

    select col1, col2, col3, col4, col5

    ,(s1+abs(s1)+s2+abs(s2)+s3+abs(s3)+s4+abs(s4)+s5+abs(s5))/2 as Positive

    ,abs((s1-abs(s1)+s2-abs(s2)+s3-abs(s3)+s4-abs(s4)+s5-abs(s5))/2) as Negative

    from (select *,sign(col1) s1,sign(col2) s2,sign(col3) s3,sign(col4) s4,sign(col5) s5

    from tablename) sn

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • This should work also:

    select

    abs(sign(col1)+sign(col2)+sign(col3)+

    sign(col4)+sign(col5)) as NegativeCount

    sign(col1)+sign(col2)+sign(col3)+

    sign(col4)+sign(col5)+5 as PositiveCount

    from

    MyTable

  • Michael Valentine Jones (11/19/2012)


    This should work also:

    select

    abs(sign(col1)+sign(col2)+sign(col3)+

    sign(col4)+sign(col5)) as NegativeCount

    sign(col1)+sign(col2)+sign(col3)+

    sign(col4)+sign(col5)+5 as PositiveCount

    from

    MyTable

    Have you tested it? You should try:

    CREATE TABLE [dbo].[MyTable](

    [col1] [int] NOT NULL,

    [col2] [int] NOT NULL,

    [col3] [int] NOT NULL,

    [col4] [int] NOT NULL,

    [col5] [int] NOT NULL

    ) ON [PRIMARY]

    insert MyTable

    select 0,-0,0,0,0

    union select -3,5,-15,6,0

    union select -3,5,-15,6,-16

    union select 10,-14,8,17,-16

    union select -11,-10,5,2,-4

    union select -6,-2,7,-13,8

    union select -4,2,-6,7,19

    union select -10,8,7,16,-14

    union select 17,-17,-9,14,18

    union select 7,19,15,12,-13

    union select 7,19,15,12,-13

    It's not so simple as appears at first glance, so check my version...;-)

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • Actually, I found even more elegant way:

    SELECT *

    FROM tablename

    CROSS APPLY(SELECT SUM(SIGN(c) + ABS(SIGN(c)))/2 AS Positive

    ,ABS(SUM(SIGN(c) - ABS(SIGN(c)))/2) AS Negative

    FROM (VALUES (col1),(col2),(col3),(col4),(col5)) c(c)) calc

    ... however, I will not be surprised if CASE WHEN outperform all of mathematical-puzzle based.

    It requires some testing, but I have no time for this right now :Whistling:

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • Eugene Elutin (11/19/2012)


    Michael Valentine Jones (11/19/2012)


    This should work also:

    select

    abs(sign(col1)+sign(col2)+sign(col3)+

    sign(col4)+sign(col5)) as NegativeCount

    sign(col1)+sign(col2)+sign(col3)+

    sign(col4)+sign(col5)+5 as PositiveCount

    from

    MyTable

    Have you tested it? You should try:

    CREATE TABLE [dbo].[MyTable](

    [col1] [int] NOT NULL,

    [col2] [int] NOT NULL,

    [col3] [int] NOT NULL,

    [col4] [int] NOT NULL,

    [col5] [int] NOT NULL

    ) ON [PRIMARY]

    insert MyTable

    select 0,-0,0,0,0

    union select -3,5,-15,6,0

    union select -3,5,-15,6,-16

    union select 10,-14,8,17,-16

    union select -11,-10,5,2,-4

    union select -6,-2,7,-13,8

    union select -4,2,-6,7,19

    union select -10,8,7,16,-14

    union select 17,-17,-9,14,18

    union select 7,19,15,12,-13

    union select 7,19,15,12,-13

    It's not so simple as appears at first glance, so check my version...;-)

    I think you meant to use "union all select" so that the last entry (a duplicate) is retained.

    Your next solution gets a +1!


    My mantra: No loops! No CURSORs! No RBAR! Hoo-uh![/I]

    My thought question: Have you ever been told that your query runs too fast?

    My advice:
    INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
    The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.

    Need to UNPIVOT? Why not CROSS APPLY VALUES instead?[/url]
    Since random numbers are too important to be left to chance, let's generate some![/url]
    Learn to understand recursive CTEs by example.[/url]
    [url url=http://www.sqlservercentral.com/articles/St

  • I tried for 6 numbers with the below query.

    select col1, col2, col3, col4, col5, Col6

    ,(s1+abs(s1)+s2+abs(s2)+s3+abs(s3)+s4+abs(s4)+s5+abs(s5)+ s6+abs(s6))/2 as Positive

    ,abs((s1-abs(s1)+s2-abs(s2)+s3-abs(s3)+s4-abs(s4)+s5-abs(s5)+ s6 - abs(s6))/2) as Negative

    from (select *,sign(col1) s1,sign(col2) s2,sign(col3) s3,sign(col4) s4,sign(col5) s5, sign(Col6) s6

    from MyTable) sn

    select *, 6-Positives as Negatives

    from (select *,(sign(col1)+sign(col2)+sign(col3)+sign(col4)+sign(col5)+sign(Col6)+ 6)/2 Positives

    from MyTable) sn

    It works fine.

    what is the secret behind this mathematical formula? It would be great if you explain it in detail.

    I am always your fan for such a mathematical formula ( The one you used to generate the sequence number by using BIT WISE & operator)

    karthik

  • dwain.c (11/19/2012)


    Eugene Elutin (11/19/2012)


    Michael Valentine Jones (11/19/2012)


    This should work also:

    select

    ...

    union select 7,19,15,12,-13

    union select 7,19,15,12,-13

    It's not so simple as appears at first glance, so check my version...;-)

    I think you meant to use "union all select" so that the last entry (a duplicate) is retained.

    Your next solution gets a +1!

    No, I really didn't, it was just cut-&-paste, the data variation above is enough for testing functionality of the query...

    Thanks for "+1", I like it too 🙂

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • karthik M (11/19/2012)


    I tried for 6 numbers with the below query.

    select col1, col2, col3, col4, col5, Col6

    ,(s1+abs(s1)+s2+abs(s2)+s3+abs(s3)+s4+abs(s4)+s5+abs(s5)+ s6+abs(s6))/2 as Positive

    ,abs((s1-abs(s1)+s2-abs(s2)+s3-abs(s3)+s4-abs(s4)+s5-abs(s5)+ s6 - abs(s6))/2) as Negative

    from (select *,sign(col1) s1,sign(col2) s2,sign(col3) s3,sign(col4) s4,sign(col5) s5, sign(Col6) s6

    from MyTable) sn

    select *, 6-Positives as Negatives

    from (select *,(sign(col1)+sign(col2)+sign(col3)+sign(col4)+sign(col5)+sign(Col6)+ 6)/2 Positives

    from MyTable) sn

    It works fine.

    what is the secret behind this mathematical formula? It would be great if you explain it in detail.

    I am always your fan for such a mathematical formula ( The one you used to generate the sequence number by using BIT WISE & operator)

    It's quite simple there.

    SIGN returns -1 for negative, 1 for positive and 0 for zero.

    So, in case if we have no 0 (zeros) in data, we only need to find count of positive numbers, as negative count = count of columns - count of positive

    As, SIGN returns negative and positive 1's, we just need to "neutralise" negative 1's and sum positive ones. What you can do to achieve it is: N + 1, where N is result of SIGN and when it's negative 1 (-1), result of the "N + 1" will be zero! However we have positive ones as well, and the above calculation will make it 2. So, we divide result of the formula by 2:

    (N + 1)/2 and get exactly what we need: It will still return 0 for -1's and 1's will stay as 1's, so we can just sum it.

    With a bit more algebra we got the following:

    (C1 + 1)/2 + (C2+1)/2 + (C3+1)/2 + (C4+1)/2 + (C5+1)/2 =

    (C1 + C2 + C3 + C4 + C5 + 5)/2

    Now, where we have 0's in data, it became a bit more complicated, as 0's shouldn't be counted as positive or negative.

    So, (N + 1)/2 does still work for counting positive (as in SQL (0+1)/2 will return 0), however algebraic transformation will no longer apply as "+5" bit will screw results. So, we need to find the way to make "1" in the "(N+1)/2)" formula to be also "neutralised" whenever N = 0. Hence use of ABS(N) instead of constant "1". You can see that ABS(-1 or 1) will still be 1 and ABS(0) will turn to 0. Actually, you may notice that for counting positive numbers, it's not really required as long formula is not simplified form it's full form of

    (C1 + 1)/2 + (C2+1)/2 + (C3+1)/2 + (C4+1)/2 + (C5+1)/2

    As (0 + 1)/2 = 0 in SQL, positive numbers count will be calculated correctly anyway.

    However, count of negative numbers is no longer equal to count of columns minus count of positive. It's need to be calculated with full ignorance of 0's.

    So we need dedicated formula to count negatives. So, we will try to apply same principals as for calculating positive's count, except now we need to "neutralise" positive ones (1) and sum negative ones (-1). To neutralise positive 1 we need subtract 1 from it: N - 1 will make it work for positives. Now, where N is negative 1 it will make it "-2", so we do the same as in our first case - divide it by 2, so we end up with (-1). The rest is the same: we sum all of -1's and take it absolute value (so negative count will turn to nice positive count ;-)).

    Now, while I was explaining all of the above, I have realised, that use of ABS(N) is not really required at all! It was only used to make sure that algebraic transformation (simplification) will not be performed, but if it's not performed explicitly, SQL is not going to do it as well, so formulas can stay in their original forms as:

    Count Of Positives = (C1 + 1)/2 + (C2+1)/2 + (C3+1)/2 + (C4+1)/2 + (C5+1)/2

    Count Of Negatives = (C1 - 1)/2 + (C2-1)/2 + (C3-1)/2 + (C4-1)/2 + (C5-1)/2

    Zero's will be "neutralised" implicitly as long we play only with INT datatypes.

    So my final version, with CROSS APPLY, will be even simpler:

    SELECT *

    FROM tablename

    CROSS APPLY(SELECT SUM((SIGN(c) + 1)/2) AS Positive

    ,ABS(SUM((SIGN(c) - 1)/2)) AS Negative

    FROM (VALUES (col1),(col2),(col3),(col4),(col5)) c(c)) calc

    _____________________________________________
    "The only true wisdom is in knowing you know nothing"
    "O skol'ko nam otkrytiy chudnyh prevnosit microsofta duh!":-D
    (So many miracle inventions provided by MS to us...)

    How to post your question to get the best and quick help[/url]

  • EE,

    Great! Thanks a lot for your time and detailed explanation!

    karthik

  • Since I never some across this kind of situation, I just know what is what.

    I have just read about sign() & abs() functions. Thats it.

    But I am seeing the real time usage in this example. Give me some more food like this in another thread.

    🙂

    karthik

  • Thanks everyone for the help.

    “If your actions inspire others to dream more, learn more, do more and become more, you are a leader.” -- John Quincy Adams

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

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