Nested CASE statement to exclude certain rows - sql

Sample Data: Oracle 10g
Number Activity
1 x Activity
1 no activity
2 x activity
3 no activity
What I need to do is produce the rows where there is either no activity or x activity, then comes the problem: If there is an ID with both x activity and no activity, I only want to produce the x activity rows. Can it be done? Here is the CASE statement that produces the above data:
CASE WHEN DSKMTB_ACTIVITY_TYPE.ACTIVITY_TYPE_LABEL IS NULL
THEN 'No Activity'
ELSE DSKMTB_ACTIVITY_TYPE.ACTIVITY_TYPE_LABEL
END AS "Activity Type"
I am thinking I need to nest a CASE statement, but I can't quite gather the logic in my head. Please let me know if there is anything that I can do to help. Per the usual, I have not included the entire query here, as it is quite large, but will edit if anyone feels it is necessary. Thanks in advance.

By grouping by ID and filtering you might get through it:
SELECT
CASE
WHEN MAX(NVL(a.ACTIVITY_TYPE_LABEL, 'aaaa')) = 'aaaa'
THEN 'No Activity'
ELSE a.ACTIVITY_TYPE_LABEL
END AS "Activity Type"
FROM DSKMTB_ACTIVITY_TYPE a
GROUP BY a.id
The problem is if you actually select other fields, it will probably break the GROUP BY and we'll have to make a subquery.

You can put a MAX() around the case statement and than a GROUP BY on the ID field.
MAX(CASE WHEN DSKMTB_ACTIVITY_TYPE.ACTIVITY_TYPE_LABEL IS NULL
THEN 'No Activity'
ELSE DSKMTB_ACTIVITY_TYPE.ACTIVITY_TYPE_LABEL
END) AS "Activity Type"
GROUP BY ID

Related

Oracle database column cannot be filtered directly

I'm working with Netsuite SuiteAnalytics Connect using Netsuite2.com odbc. I'm seeking to query the transactions table, while filtering to specific transaction statuses. Netsuite, for whatever reason, requires that you work with the status codes rather than the status descriptions. I am interested in four status codes including 'B', corresponding to "Pending Fulfillment"
During testing I'm working with a transaction with ID = xxxxx.
I use the alias t for the Transaction table. Querying the table with t.ID = xxxxx gives a result (5 different transaction lines).
t.STATUS returns 'B'. TO_CHAR(t.STATUS) returns 'B'. SUBSTR(t.STATUS, 1, 1) returns 'B'. LENGTH(t.STATUS) returns 1.
However, if I use the condition t.ID = xxxxx AND t.STATUS = 'B', or the TO_CHAR or SUBSTR options, then I get nothing returned. t.STATUS LIKE '%B%' works.
How can that possibly be? Does anyone know what is happening here? Since I am interested in four different status codes, the LIKE method seems like a bad idea and I'd prefer not to use the fundamentally worse option without knowing the background.
Edit: CASE WHEN t.STATUS = 'B' THEN 'T' ELSE 'F' END AS IS_B returns 'T'
Condition t.STATUS LIKE '%B' works. t.STATUS LIKE 'B%' does not. I'm thinking that it is testing against the status 'Sales Order: B' but not actually reporting that when it returns the column. What a mess. I'd need to intuit the syntax of that but I'll try and report back.
Within "Where", you have to use
t.STATUS = 'SalesOrd:B'
If you are testing during the column descriptions you have to use t.STATUS = 'B'. Thank you Oracle.

Why are my SQL subqueries getting syntax errors? Using COUNT()

The question is: are there more reviews with the word "love" or with the word "hate" in them?
Here is the code I am trying to use:
SELECT
(COUNT(SELECT id FROM review WHERE text LIKE '%love%') FROM review) AS NumLove,
(COUNT(SELECT id FROM review WHERE text LIKE '%hate%') FROM review) AS NumHate
FROM
review
Another variation:
SELECT
COUNT(SELECT id FROM review WHERE text LIKE '%love%') AS NumLove,
COUNT(SELECT id FROM review WHERE text LIKE '%hate%') AS NumHate
FROM
review
I am getting an error:
Syntax error near "SELECT"
I have tried to adjust the code a few different times, but I keep getting syntax errors. I can run either of the select statements on their own, and they will execute. I just can't get them to run in a single query.
You need conditional aggregation:
SELECT COUNT(CASE WHEN text LIKE '%love%' THEN 1 END) AS NumLove,
COUNT(CASE WHEN text LIKE '%hate%' THEN 1 END) AS NumHate
FROM review
The important thing to remember is functions are used against columns not result sets. Multiple ways to get there. Aside from the CASE example.
SELECT COUNT(Pro.Love) AS NumLov
FROM
(SELECT id Love FROM review WHERE text LIKE '%love%') as Pro
To write this in one query, you could use a case expression to turn your love and hate evals into 1 and 0 and use Sum instead of count.
SELECT SUM(results.love) AS NumLov
FROM
(
SELECT CASE WHEN text like '%love%' THEN 1 ELSE 0 END AS love
FROM review
) as results
I prefer to use sum instead of count here when using a condition case expression - since you really are adding up (summing) the number of 1s from each case expression
select
Sum(case when text like '%love%' then 1 else 0 end) as NumLove,
Sum(case when text like '%hate%' then 1 else 0 end) as NumHate
from review

SQL Group By and window function

I am struggling a little bit with an SQL Statement and was hoping if someone would be willing to point me in the right direction.
Below is a picture of my table:
As you can see here I have a column called 'State'. Basically I want to group all 'State' associated with a particular BuildID and display them in an extra column on my SQL output.
I am close but not quite there.
In the next picture below is an example of the statement I have used to try and achieve this:
As you can see here, it has done SUM and added the TotalTime and created the extra columns I require. However instead of grouping it all into one record, it has created 2 extra lines per BuildID. One with the value for the 'Running' state, another record for the value State of 'Break' and another record that contains the value 0 on both States.
Now what I want it to do is group it into one record for each BuildID. Like the picture I have added below:
The above image is how I want the records displayed. But the problem is I have added the WHERE [State] = Running, which I know is wrong but was just using this as an example. As you can see the 'Break' column has no value.
I hope this makes sense and was hoping if someone could point me in the right direction?
Here is an example on SQL fiddle http://sqlfiddle.com/#!3/7b6b9/2
Thanks for taking the time to read this :)
Have refined the sql a bit, here you go:
SELECT BuildID,Product, Program,
sum(CASE WHEN State = 'Running' THEN cast(TotalTime as INT) ELSE 0 END) AS Running
, sum(CASE WHEN State = 'Break' THEN cast(TotalTime as INT) ELSE 0 END) AS Breakt
FROM Line1Log
GROUP BY BuildID,Product, Program
Please check SQLFiddle
Not sure if i am missing something :-), but the solution looks pretty straight forward. Let me know.
move your SUM() OVER(PARTITION BY...) out from CASE
SQL fiddle
select BuildID, Product, Program, Sum(Running) Running, Sum([Break]) [Break]
from (
SELECT Distinct BuildID, Product, Program,
Sum(Case when [State]='Running' Then TotalTime Else 0 END) OVER (Partition by [State], [BuildID]) Running,
Sum(Case when [State]='Break' Then TotalTime Else 0 END) OVER (Partition by [State], [BuildID]) [Break]
From Line1Log) T
group by BuildID, Product, Program
Everything else is ok, why are you grouping all columns?, Try this,
Select BuidId,Product,program,Sum(Running),Sum(Break)
(......
.....
......) as T
group by BuidId,Product,program

Changing position of a row in sql

In the above t-sql table I would like very much for the Total row to appear at the bottom. I have been wracking my head against this and in all of my other Queries simply using ORDER BY Status works as Total is alphabetically much farther down the list than most of our row values.
This is not the case here and I just can't figure out how to change it
I'm pretty new to sql and I'be been having a lot of difficulty even determining how to phrase a google search. So far I've just gotten results pertaining to Order By
The results of a select query, unless an order is explicitly specified via an 'order by' clause, can be returned in any order. Moreover, the order in which they are returned is not even deterministic. Running the exact same query 3 times in succession might return the exact same result set in 3 different orderings.
So if you want a particular order to your table, you need to order it. An order by clause like
select *
from myTable t
where ...
order by case Status when 'Total' then 1 else 0 end ,
Status
would do you. The 'Total' row will float to the bottom, the other rows will be ordered in collating sequence. You can also order things arbitrarily with this technique:
select *
from myTable t
where ...
order by case Status
when 'Deceased' then 1
when 'Total' then 2
when 'Active' then 3
when 'Withdrawn' then 4
else 5
end
will list the row(s) with a status of 'Deceased' first, followed by the row(s) with a status of 'Total', then 'Active' and 'Withdrawn', and finally anything that didn't match up to an item in the list.
ORDER BY CASE WHEN STATUS = 'Total' THEN 'zzz' ELSE STATUS END
In SQL Server (and most other databases), you can use case to sort certain statūs above others:
order by
case Status
when 'Total' then 2
else 1
end
, Status
In MS Access, you can use iif:
order by
iif(Status = 'Total', 2, 1)
, Status
You can use conditional expressions in order by:
order by (case when status = 'Total' then 1 else 0 end),
status

How to get aggregates without using a nested sql query

I am writing a custom report from an Avamar (Postgresql) database which contains backup job history. My task is to display jobs that failed last night (based on status_code), and include that client's success ratio (jobs succeeded/total jobs run) over the past 30 days on the same line.
So the overall select just picks up clients that failed (status_code doesn't equal 30000, which is the success code). However, for each failed client from last night, I need to also know how many jobs have succeeded, and how many jobs total were started/scheduled in the past 30 days. (The time period part is simple, so I haven't included it in the code below, to keep it simple.)
I tried to do this without using a nested query, based on Hobodave's feedback on this similar question but I'm not quite able to nail it.
In the query below, I get the following error:
column "v_activities_2.client_name" must appear in the GROUP BY clause or be used in an aggregate function
Here's my (broken) query. I know the logic is flawed, but I'm coming up empty with how best to accomplish this. Thanks in advance for any guidance!
select
split_part(client_name,'.',1) as client_name,
bunchofothercolumnns,
round(
100.0 * (
((sum(CASE WHEN status_code=30000 THEN 1 ELSE 0 END))) /
((sum(CASE WHEN type='Scheduled Backup' THEN 1 ELSE 0 END))))
as percent_total
from v_activities_2
where
status_code<>30000
order by client_name
You need to define a GROUP BY if you have columns in the SELECT that do not have aggregate functions performed on them:
SELECT SPLIT_PART(t.client_name, '.', 1) AS client_name,
SUM(CASE WHEN status_code = 30000 THEN 1 ELSE 0 END) as successes
FROM v_activities_2
GROUP BY SPLIT_PART(t.client_name, '.', 1)
ORDER BY client_name
How do you expect the following to work:
SUM(CASE WHEN status_code = 30000 THEN 1 ELSE 0 END) as successes
FROM v_activities_2
WHERE status_code <> 30000
You can't expect to count rows you're excluding.
Why avoid nested query?
It seems most logical / efficient solution here.
If you do this in one pass with no sobqueries (only group by's), you will end with scanning the whole table (or joined tables) - which is not efficient, because only SOME clients failed last night.
Subqueries are not that bad, in general.