Choose grouping value based on condition - sql

I need to construct a query which meets the following criteria:
select X columns and MAX(column1), group by X columns
if column1 contains negative value (can be only one in given group), then instead of MAX(column1) display 'reset'
I know I can do it by using case before grouping and substituting negative values with a very large number and then using case after grouping, but this is an awfully messy solution.
Does anybody have a better idea how to solve this?

You can additionally select the minimum value and check that for negativity.
Something like the following:
select foo, case when min_value < 0 then 'reset' else to_char(max_value) end
from
(
select foo, max(column1) as max_value, min(column1) as min_value
from your_table
group by foo
)
Or, without the sub query:
select foo, case when min(column1) < 0 then 'reset' else to_char(max(column1)) end
from your_table
group by foo

Related

Why Using COALESCE or CASE keep returning null

I have the following SQL Query :
(SELECT ROUND(SUM(NBTOSUM)/1000000,1) FROM MyTable t2 WHERE t2.ELEMNAME IN ('A','B','C'))
Which works fine.
But Where there is no 'A','B','C' the result of the select is (null)
So to handle it, I did the following :
(SELECT COALESCE(ROUND(SUM(NBTOSUM)/1000000,1),0) FROM MyTable t2 WHERE t2.ELEMNAME IN ('A','B','C'))
And also try :
(SELECT
CASE
WHEN SUM(NBTOSUM)/1000000 IS NULL THEN 0
ELSE ROUND(SUM(NBTOSUM)/1000000,1)
END
FROM MyTable t2 WHERE t2.ELEMNAME IN ('A','B','C'))
But both keep returning null
What am I doing wrong ?
Move the WHERE restrictions to the CASE expression as well:
SELECT ROUND(SUM(CASE WHEN t2.ELEMNAME IN ('A','B','C')
THEN NBTOSUM ELSE 0 END) / 1000000, 1)
FROM MyTable t2;
Note that this trick solves the null problem and also avoids the need for an ugly COALESCE() call.
Your code should work as the SUM aggregation function will generate a single row of output regardless of whether the number of input rows is zero or non-zero. If there are no input rows or the values are all NULL then the output of the SUM will be NULL and then COALESCE would work.
Since you claim it does not then that suggests that there is something else going on in your query that you have not shared in the question.
You have braces around your statement suggesting that you are using it as part of a larger statement. If so, you can try moving the COALESCE to the outer query:
SELECT COALESCE(
(
SELECT ROUND(SUM(NBTOSUM)/1000000,1)
FROM MyTable
WHERE ELEMNAME IN ('A','B','C')
),
0
)
FROM your_outer_query;
That might fix the problem if you are somehow correlating to an outer query but your question makes no mention of that.
fiddle

SQL: identify if there are multiples (not duplicates) in a column

I am currently struggling in identifying a possibility to identify certain patterns in my data using SSMS.
I wish to identify rows that contain multiples (x2, x3, or x*4) of an entry within the same column.
I really have no clue on how to even start my "where" statement right now.
SELECT [numbers], [product_ID]
FROM [db].[dbo].[tablename]
WHERE [numbers] = numbers*2
My problem is that with the code above I can obviously only identify zeros.
Google only helps me out with finding duplicates but I can't find a way to identify multiples of a value...
My desired result would be a table that only contains numbers (linked to product_IDs) that are multiples of each other
Anyone can help me out here?
If a column contains multiples, then all are multiples of the smallest non-zero value. Let me assume the values are positive or zero for this purpose.
So, you can determine if this is the case using window functions and modulo arithmetic:
select t.*
from (select t.*,
min(case when number > 0 then number end) over () as min_number
from t
) t
where number % min_number = 0 or min_number = 1;
If you want to know if all numbers meet this criteria, use aggregation:
select (case when min(number % min_number) = 0 then 'all multiples' else 'oops' end)
from (select t.*,
min(case when number > 0 then number end) over () as min_number
from t
) t
My desired result would be a table that only contains numbers (linked to product_IDs) that are multiples of each other
You'll need to test all pairs of rows, which means a CROSS JOIN.
Something like this:
with q as
(
SELECT [numbers],
[product_ID],
cast(a.numbers as float) / coalesce(b.numbers, null) ratio
FROM [tablename] a
CROSS JOIN [tablename] b
)
select *
from q
where ratio = cast(ratio as bigint)
and ratio > 1

How to calculate an average in SQL excluding zeroes?

I want to calculate the average of a column of numbers, but i want to exclude the rows that have a zero in that column, is there any way this is possible?
The code i have is just a simple sum/count:
SELECT SUM(Column1)/Count(Column1) AS Average
FROM Table1
SELECT AVG(Column1) FROM Table1 WHERE Column1 <> 0
One approach is AVG() and CASE/NULLIF():
SELECT AVG(NULLIF(Column1, 0)) as Average
FROM table1;
Average ignores NULL values. This assumes that you want other aggregations; otherwise, the obvious choice is filtering.
Multiple roads lead to Rome...
select sum(Column1) / sum( case Column1
case 0 then 0 else 1
end )
from table1
SELECT AVG(column) FROM TABLE
where column **not like** '0%'

How Do I Use Case Statement Column In Group By

As stated by the question, I'm trying to formulate a query that has a case statement in the column results, and then I want to include that column in the query's group by statement. To give a concrete example, here is all little of what my query looks like:
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
GROUP BY SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2, CASE_COLUMN
Before coming here, I checked out a few websites, including this one, to try solve my problem. I've tried adding another alias after the CASE keyword like is shown in the linked web page but have had no luck. The error message I continue to receive is the following:
[Error] Script lines: 127-151 ----------------------
CASE_COLUMN IS NOT VALID IN THE CONTEXT WHERE IT IS USED. SQLCODE=-206, SQLSTATE=42703, DRIVER=3.53.71
Has anyone else run into the issues I'm facing and been able to use a GROUP BY on the results of a CASE statement? Any help would be appreciated. Oh, and the DB2 version is a z/OS instance, version 10 (DSN10015)
The alias isn't available to use in the GROUP BY because when GROUP BY happens the alias isn't defined yet:
Here's the order:
1.FROM
2.WHERE
3.GROUP BY
4.HAVING
5.SELECT
6.ORDER BY
You can work around that with:
SELECT column1,column2,case_column
FROM (
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
) a
GROUP BY COLUMN1, COLUMN2, CASE_COLUMN
Or just use the case you use in SELECT in GROUP BY
You can either use the case as is in the group by, like this:
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
GROUP BY SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END
or use a sub-query like this:
select COLUMN1, COLUMN2, CASE_COLUMN
from (
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
) a
GROUP BY COLUMN1, COLUMN2, CASE_COLUMN

How to summarize SQL table to return value conditionally

I have a table with several rows, and several columns. It looks like this:
Name Description
X PASS
X PASS
X FAIL
I want it to return only one row. If all of them are PASS, return PASS.
If one or more of them are FAIL, then return FAIL.
What's the best way to go about achieving this in SQL Server 2008?
EDIT: The values in the name column will always be the same.
Depending on the database indexes, and assuming you want one row returned per unique name, I would look at the performance of
select
name,
min([description]) as description
from
tableA
group by
name
compared to the other solutions
SELECT TOP 1 CASE Description WHEN 'FAIL' THEN 'FAIL' ELSE 'PASS' END
FROM DaTable
ORDER BY Description
OP: Is it possible that the table is empty? In that case this query won't return any rows, obviously.
EDIT
According to aquinas's comment I created a modified query without ordering:
SELECT CASE COUNT(Description) WHEN 0 THEN 'FAIL' ELSE 'PASS' END
FROM DaTable
WHERE Description = 'FAIL'
This query will return PASS if DaTable is empty.
This is the simplest solution you will find:
SELECT MIN(Description) FROM tbl
If there's at least one FAIL, then our result column will contain FAIL, otherwise, it will contain PASS.
You can use EXISTS to get the existance of a row containing "FAIL".
You could also try something like:
SELECT TOP 1 COALESCE(tFail.Description,t.Description)
FROM myTable AS t
LEFT JOIN myTable AS tFail ON tFail.Name = t.Name AND tFail.Description = 'FAIL'
WHERE t.Name = 'x'
Here is the query:
--DROP TABLE result
CREATE TABLE result(Name varchar(10),Description varchar(20))
--select * from result
INSERT INTO result
VALUES('X','PASS'),('X','PASS'),('X','FAIL')
;WITH CTE(descp,cnt) as (SELECT [description],COUNT(*) as cnt FROM result group by [description])
SELECT CASE WHEN COUNT(*) > 1 then 'FAIL' when COUNT(*)=1 then MAX(descp) else 'PASS' END FROM CTE