Aggregate sub-query not producing expected results - sql

I have two tables and there's a one to many relationship between the two. My query has a group by and I want to determine if there are any results in the 2nd table that match some criteria. I couldn't figure it out with a sub-query and tried the following code, but it's not giving you the results you expect.
CASE WHEN
(SELECT SUM(CASE WHEN a.ContentId IS NULL THEN 1 ELSE 0 END)) > 0
THEN 1
ELSE 0
END as 'HasAttachments'
Basically, I am trying to figure out if my message (which could have many attachments) has any attachments where the ContentId is null and if that count is greater than 0 then I want to return the boolean value in HasAttachments.
Any help would be great!

CASE WHEN
SUM(CASE WHEN a.ContentId IS NULL
AND a.message_id IS NOT NULL
THEN 1
ELSE 0
END) > 0
THEN 1
ELSE 0
END as HasAttachments
From your narratives it seems like your inner Case Statement is missing another condition to determine when there is actually an attachment. If you don't put in the second condition of when the AttachmentTable.message_id IS NOT NULL then you will count both messages that don't have any attachments and those messages that have attachments but no content id as the same thing. But adding the a.message_id you limit that to just the case you seem to desire from your narrative.

Here is one way I might phrase the query:
select m.*,
(case when exists (select 1 from attachments a where a.message_id = m.message_id and content_id is null
)
then 1 else 0
end) as HasAttachments
from messages m;
It is unclear why your query doesn't work, without the context of the rest of the query.

Related

Why does this not return 0

I have a query like:
select nvl(nvl(sum(a.quantity),0)-nvl(cc.quantityCor,0),0)
from RCV_TRANSACTIONS a
LEFT JOIN (select c.shipment_line_id,c.oe_order_line_id,nvl(sum(c.quantity),0) quantityCor
from RCV_TRANSACTIONS c
where c.TRANSACTION_TYPE='CORRECT'
group by c.shipment_line_id,c.oe_order_line_id) cc on (a.shipment_line_id=cc.shipment_line_id and a.shipment_line_id=7085740)
where a.transaction_type='DELIVER'
and a.shipment_line_id=7085740
group by nvl(cc.quantityCor,0);
The query runs OK, but returns no value. I want it to return 0 if there is no quantity found. Where have I gone wrong?
An aggregation query with a GROUP BY returns no rows if all rows are filtered out.
An aggregation query with no GROUP BY always returns one row, even if all rows are filtered out.
So, just remove the GROUP BY. And change the SELECT to:
select coalesce(sum(a.quantity), 0) - coalesce(max(cc.quantityCor), 0)
I may be wrong, but it seems you merely want to subtract CORRECT quantity from DELIVER quantity for shipment 7085740. You don't need a complicated query for that. Especially your GROUP BY clauses make no sense if that is what you are after.
One way to write this query would be:
select
sum(case when transaction_type = 'DELIVER' then quantity else 0 end) -
sum(case when transaction_type = 'CORRECT' then quantity else 0 end) as diff
from rcv_transactions
where shipment_line_id = 7085740;
I had a query like this and was trying to return 'X' when the item is not valid.
SELECT case when segment1 is not null then segment1 else 'X' end
--INTO v_orgValidItem
FROM mtl_system_items_b
WHERE segment1='1676001000'--'Jul-00'--l_item
and organization_id=168;
..but it was returning NULL.
Changed to use aggregation with no group by and now it returns 'X' when the item is not valid.
SELECT case when max(segment1) is not null then max(segment1) else 'X' end valid
--INTO v_orgValidItem
FROM mtl_system_items_b
WHERE segment1='1676001000'--'Jul-00'--l_item
and organization_id=168;--l_ship_to_organization_id_pb;
Here is another example, proving the order of operations really matters.
When there is no match for this quote number, this query returns NULL:
SELECT MAX(NVL(QUOTE_VENDOR_QUOTE_NUMBER,0))
FROM PO_HEADERS_ALL
WHERE QUOTE_VENDOR_QUOTE_NUMBER='foo.bar';
..reversing the order of MAX and NVL makes all the difference. This query returns the NULL value condition:
SELECT NVL(MAX(QUOTE_VENDOR_QUOTE_NUMBER),0)
FROM PO_HEADERS_ALL
WHERE QUOTE_VENDOR_QUOTE_NUMBER='foo.bar';

Using a case statement to show the count of two types of values in a column

SELECT
qt.name,
CASE
WHEN qr.isfinished = 0 THEN COUNT(qr.resultid)
END AS 'Attempted',
CASE
WHEN qr.isfinished = 1 THEN COUNT(qr.resultid)
END AS 'Completed'
Need it to show attempted and completed values on the same row
Name attempted Completed
--------------------------------
Algebra I 114 NULL
Algebra II 47 NULL
ASVAB 55 NULL
Algebra I NULL 69
Algebra II NULL 55
ASVAB NULL 84
Thank you for the help!
If isfinished is bit, you can't aggregate on it.
And the CASE goes inside the COUNT
SELECT qt.name,
count(Case when qr.isfinished = 0 THEN 1 END) as 'Attempted',
count(Case when qr.isfinished = 1 THEN 1 END) as 'Completed'
FROM
...
GROUP BY
qt.name
There are multiple ways you could do this, for example you could do this with joins, or you can use group by -- like so:
SELECT
qt.name,
SUM(CASE qr.isfinsihed WHEN 1 THEN 1 ELSE 0) AS 'Attempted',
SUM(CASE qr.isfinished WHEN 0 THEN 1 ELSE 0) AS 'Completed'
FROM -- what ever your from clause is, it goes here --
GROUP BY
qt.name
In order to have them on the same row, you will need to group by what they have in common. From what you have given in the question, I am assuming that is the qt.name.
Next, you can use the SUM aggregate to get each field count. All of the records that meet the criteria for each item count towards the sum, the others don't. You can also use count with 1's and Null's, I prefer using Sum because it can allow for weighted totals if I need them.

Stuck on a slightly tricky query, trying to ignore multiple results based on a single field

Here is a simple database representation of what I'm stuck on:
IDNumber TimeSpent Completed
1 0 No
1 0 No
1 2 No
2 0 No
3 0 No
I'm currently querying the database as such...
"SELECT Distinct (IDNumber) AS Info FROM TestTable
ORDER BY WorkOrderNumber";
And it gives me back the results
1
2
3
Which is expected.
Now, I'd like to adjust it to where any instance of an IDNumber that have TimeSpent != 0 or Completed != No means that the IDNumber isn't grabbed at all. So for example in the database given, since TimeSpent = 2, I don't want IDNumber 1 to be returned in my query at all.
My first instinct was to jump to something like this...
"SELECT Distinct (IDNumber) AS Info FROM TestTable
WHERE TimeSpent='0' AND Completed='No'
ORDER BY WorkOrderNumber";
But obviously that wouldn't work. It would correctly ignore one of the IDNumber 1's but since two others still satisfy the WHERE clause it would still return 1.
Any pointers here?
SELECT DISTINCT IDNumber
FROM TestTable
WHERE IDNumber NOT IN
(SELECT IDNUmber FROM TestTable WHERE TimeSPent <> 0 OR Completed <> 'No')
You can do this with an aggregation, using a having clause:
select IDNumber
from TestTable
group by IDNumber
having sum(case when TimeSpent = 0 then 1 else 0 end) = 0 and
sum(case when Completed = 'No' then 1 else 0 end) = 0
The having clause is counting the number of rows that meet each condition. The = 0 is simply saying that there are no matches.
I prefer the aggregation method because it is more flexible in terms of the conditions that you can set on the groups.

How to count the rows which contains non zero values in sql

SELECT round(COUNT(dmd_1wk),2) AS NBR_ITEMS_1WK
FROM table;
Field dmd_1wk has so many zeros in it. How do I Count the non zero values?
It sounds like you just need to add a WHERE clause:
SELECT
round(COUNT(dmd_1wk),2) AS NBR_ITEMS_1WK
FROM table
WHERE dmd_1wk <> 0;
If you want the count of both non-zero and zero values, then you can use something like:
SELECT
round(COUNT(case when dmd_1wk <> 0 then dmd_1wk end),2) AS NBR_ITEMS_1WK_NonZero,
round(COUNT(case when dmd_1wk = 0 then dmd_1wk end),2) AS NBR_ITEMS_1WK_Zero
FROM table;
Method 1: Case Statement. This may be useful if you need to continue to process all rows (which a where clause would prevent).
SELECT count(case when dmd_1wk = 0 then 0 else 1 end) as NonZeroCount FROM MyTable
Method 2: Where Clause.
SELECT
count(1) as NonZeroCount
FROM
MyTable
WHERE
dmd_1wk <> 0
I'd like to offer another solution using NULLIF since COUNT won't count NULL values:
SELECT round(COUNT(NULLIF(dmd_1wk,0)),2) AS NBR_ITEMS_1WK
FROM table;
And here is the Fiddle.
Good luck.
Methinks bluefeets answer is probably what you are really looking for, as it sounds like you just want to count non-zeros; but this will get you a count of zero and non-zero items if that's not the case:
SELECT
ROUND(SUM(CASE NVL(dmd_1wk, 0) = 0 THEN 1 ELSE 0 END), 2) AS "Zeros",
ROUND(SUM(CASE NVL(dmd_1wk, 0) != 0 THEN 1 ELSE 0 END), 2) AS "NonZeros"
FROM table
Although there is no point in rounding a whole number, I've included your original ROUNDs as I'm guessing you're using it for formatting, but you might want to use:
TO_CHAR(SUM(...), '999.00')
as that's the intended function for formatting numbers.
You can filter them.
SELECT round(COUNT(dmd_1wk),2) AS NBR_ITEMS_1WK
FROM table
WHERE dmd_1wk <> 0;

SQL 2 counts with different filter

I have a table and I need calculate two aggregate functions with different conditions in one statement. How can I do this?
Pseudocode below:
SELECT count(CoumntA) *< 0*, count(CoumntA) * > 0*
FROM dbo.TableA
This is the same idea as tombom's answer, but with SQL Server syntax:
SELECT
SUM(CASE WHEN CoumntA < 0 THEN 1 ELSE 0 END) AS LessThanZero,
SUM(CASE WHEN CoumntA > 0 THEN 1 ELSE 0 END) AS GreaterThanZero
FROM TableA
As #tombom demonstrated, this can be done as a single query. But it doesn't mean that it should be.
SELECT
SUM(CASE WHEN CoumntA < 0 THEN 1 ELSE 0 END) AS less_than_zero,
SUM(CASE WHEN CoumntA > 0 THEN 1 ELSE 0 END) AS greater_than_zero
FROM
TableA
The time when this is not so good is...
- There is an index on CoumntA
- Most values (50% or more feels about right) are exactly zero
In that case, two queries will be faster. This is because each query can use the index to quickly home in on the section to be counted. In the end only counting the relevant records.
The example I gave, however, scans the whole table every time. Only once, but always the whole table. This is worth it when you're counting most of the records. In your case it looks liek you're counting most or all of them, and so this is probably a good way of doing it.
It is possible to do this in one select statement.
The way I've done it before is like this:
SELECT SUM(CASE WHEN ColumnA < 0 THEN 1 END) AS LessThanZero,
SUM(CASE WHEN ColumnA > 0 THEN 1 END) AS GreaterThanZero
FROM dbo.TableA
This is the correct MS SQL syntax and I believe this is a very efficient way of doing it.
Don't forget you are not covering the case when ColumnA = 0!
select '< 0' as filter, COUNT(0) as cnt from TableA where [condition 1]
union
select '> 0' as filter, COUNT(0) as cnt from TableA where [condition 2]
Be sure that condition 1 and condition 2 create a partition on the original set of records, otherwise same records could be counted in both groups.
For SQL Server, one way would be;
SELECT COUNT(CASE WHEN CoumntA<0 THEN 1 ELSE NULL END),
COUNT(CASE WHEN CoumntA>0 THEN 1 ELSE NULL END)
FROM dbo.TableA
Demo here.
SELECT
SUM(IF(CoumntA < 0, 1, 0)) AS lowerThanZero,
SUM(IF(CoumntA > 0, 1, 0)) AS greaterThanZero
FROM
TableA
Is it clear what's happening? Ask, if you have any more questions.
A shorter form would be
SELECT
SUM(CoumntA < 0) AS lowerThanZero,
SUM(CoumntA > 0) AS greaterThanZero
FROM
TableA
This is possible, since in MySQL a true condition is equal 1, a false condition is equal 0
EDIT: okay, okay, sorry, don't know why I thought it's about MySQL here.
See the other answers about correct syntax.