Zero records for different when conditions - sql

How do i display zero record in sqlserver if i dont find anything
select count(*) as[count],
case when status=0 then 'pending'
when status=1 then 'running'
when status=2 then 'completed'
end as status
from Queue where ID='APP'
group by status
what i want is if there is no record for status=0, how should i display 0 in the result also for other status as well. I need something like pending=0,running=0 and completed=0 if no rows found for respective status.
I tried something like below, but that does not suit my need. Any Idea?
if exists (myselect query)
select 1
else
select 0

Use values() and a left join:
select s.descr, count(q.id) cnt
from (values (0, 'pending'), (1, 'running'), (2, 'completed')) s(status, descr)
left join queue q on q.status = s.status and q.id = 'APP'
group by s.descr
This generates one row per status, with the count of rows in such status in table queue.

Related

Partition table based on joined table

We have 2 Tables Lead and Task.
One lead can have multiple Tasks.
We want to determine if a Lead has a Task who's description contains String 'x'.
If the Lead has the String the it should belong to group1 if it doesn't to group2.
Then we want to count the leads per group and week.
The problem we have is that if a Lead has several tasks and one of them has string 'x' in its description and the others don't it is counted in both groups.
We would need something that resembles a break; statement in the IFF clause of the subquery, so that if the first condition = Contain string x is satisfied the other tasks are not counted anymore.
How would we achieve that?
So far we have the following statement:
--SQL:
SELECT LeadDate, GROUP, COUNT(LEAD_ID_T1)
FROM LEAD Lead INNER JOIN
(SELECT DISTINCT LEAD.ID AS LEAD_ID_T1,
IFF(CONTAINS(Task.DESCRIPTION,
'x'),
'GROUP1',
'GROUP2') AS GROUP
FROM TASK Task
RIGHT JOIN LEAD ON TASK.WHO_ID = LEAD.ID
) T1 ON T1.LEAD_ID_T1 = LEAD.ID
GROUP BY LeadDate,GROUP;
Code breaks because it can not aggregate the measures.
Really thankful for any input. This has been bothering me for a few days now.
I am thinking EXISTS with a CASE expression:
select l.*,
(case when exists (select 1
from task t
where t.who_id = l.id and
t.description like '%x%'
)
then 'GROUP1' else 'GROUP2'
end) as the_group
from lead l;
You can also try something like this, CASE with 1 and 0 then take the SUM
SELECT LeadDate,
sum(CASE When t.description like '%x%'then 1 else 0 end) as Group1,
sum(CASE When t.description like '%x%'then 0 else 1 end) as Group2
FROM TASK t
RIGHT JOIN LEAD l ON t.WHO_ID = l.ID
GROUP BY LeadDate;

How to add new column to table with the value corresponding to the same table?

There is a status column in my table with int values. How can I assign a value to the int or do I have to create a new column in the table?
I have tried to ALTER table but what is the best method?
select status from table1;
If I run the above query we get -
id status
1 1
2 2
3 1
4 5
I want to get output -
id status
1 Accepted
2 Completed
3 Accepted
4 Declined
USE case expression, postgres
select status,
case
when status=1 then 'Accepted'
when status=2 then 'Completed'
when status=3 then 'Accepted'
when sttaus=4 then 'Declined'
end mystatus
from table1;
You can use case, refer to this SO Question
PostgreSQL CASE ... END with multiple conditions. The query will look something like this:
SELECT
id,
CASE
WHEN (status = 1) THEN 'Accepted'
WHEN status=2 then 'Completed'
WHEN status=3 then 'Accepted'
WHEN sttaus=4 then 'Declined'
END AS status
FROM table1 ;
The correct case expression would be:
select id,
(case status
when 1 then 'Accepted'
when 2 then 'Completed'
when 5 then 'Declined'
end) as status
from table1;
You can also do this with a join to a derived table:
select t1.id, v.status
from table1 t1 left join
(value (1, 'Accepted'), (2, 'Completed'), (5, 'Declined')
) v(status_int, status)
on t1.status = v.status_int;
I mention this because you should probably have a reference table for the status values. In this case, the reference table is created on the fly in the query. But it should probably be a real table in the database.

Get table rows by multiple/end column value status

I have a table with example data like below:
InquiryId status
-------------------
inquiry1 New
inquiry1 Started
inquiry1 Done
inquiry2 New
inquiry2 Pending
inquiry3 New
inquiry3 Started
inquiry4 New
inquiry4 Cancelled
..and so on
Each inquiry starts with status as New and can reach either of the end status'es (Done, Cancelled..) via the middle status'es (Started, Pending..)
The question is how do find the list of inquiries that started with New but never reached the end status, i.e. what SQL query needs to be written to get the list of uncompleted inquiries.
P.S: Using Sybase Database
You can user the following query:
select distinct InquiryId
from <table_name>
where status not in('Done','Cancelled');
It will return all all the InquiryId which doesn't have the status as Done or Cancelled and is still in intermediate state.
You can use not exists :
select t.*
from table t
where status = 'New' and
not exists (select 1
from table t1
where t1.InquiryId = t.InquiryId and
t1.status in ('Done', 'Cancelled')
);
I would use aggregation:
select inquiryId
from t
group by inquiryId
having sum(case when status = 'New' then 1 else 0 end) > 0 and
sum(case when status in ('Done', 'Cancelled') then 1 else 0 end) = 0;
The conditions in the having clause count the number of rows for each inquiryId that meet the given conditions. The > 0 says that the inquiryId has at least one row. The = 0 says that the inquiryId has more than one row.

Sql query to find count with a difference condition and total count in the same query

Here is a sample table I have
Logs
user_id, session_id, search_query, action
1, 100, dog, A
1, 100, dog, B
2, 101, cat, A
3, 102, ball, A
3, 102, ball, B
3, 102, kite, A
4, 103, ball, A
5, 104, cat, A
where
miss = for the same user_id and same session id , if action A is not followed by action B its termed a miss.
Note: action B can happen only after action A has happened.
I am able to find the count of misses for each unique search_query across all users and sessions.
SELECT l1.search_query, count(l1.*) as misses
FROM logs l1
WHERE NOT EXISTS
(SELECT NULL FROM logs l2
WHERE l1.user_id = l2.user_id
AND l1.session_id = l2.session_id
AND l1.session_id != ''
AND l2.action = 'B'
AND l1.action = 'A')
AND l1.action='A'
AND l1.search_query != ''
GROUP BY v1.search_query
order by misses desc;
I am trying to find the value of miss_percentage=(number of misses/total number of rows)*100 for each unique search_query. I couldn't figure out how to find the count with a condition and count without that condition in the same query. Any help would be great.
expected output:
cat 100
kite 100
ball 50
One way to do it is to move the EXISTS into the count
SELECT l1.search_query, count(case when NOT EXISTS
(SELECT 1 FROM logs l2
WHERE l1.user_id = l2.user_id
AND l1.session_id = l2.session_id
AND l1.search_query = l2.search_query
AND l2.action = 'B'
AND l1.action = 'A') then 1 else null end
)*100.0/count(*) as misses
FROM logs l1
WHERE l1.action='A'
AND l1.search_query != ''
GROUP BY l1.search_query
order by misses desc;
This produces the desired results, but also zeros if no misses were found. This can be removed with a HAVING clause, or postprocessing.
Note I also added the clause l1.search_query = l2.search_query that was missing, since otherwise it was counting kite as succeeded, since there is a row with B in the same session.
I think you just need to use case statements here. If I have understood your problem correctly .. then the solution would be something like this -
WITH summary
AS (
SELECT user_id
,session_id
,search_query
,count(1) AS total_views
,sum(CASE
WHEN action = 'A'
THEN 1
ELSE 0
END) AS action_a
,sum(CASE
WHEN action = 'B'
THEN 1
ELSE 0
END) AS action_b
FROM logs l
GROUP BY user_id
,session_id
,search_query
)
SELECT search_query
,(sum(action_a - action_b) / sum(action_a)) * 100 AS miss_percentage
FROM summary
GROUP BY search_query;
You can allways create two queries, and combine them into one with a join. Then you can do the calculations in the bridging (or joining) SQL statement.
In MS-SQL compatible SQL this would be:
SELECT ActiontypeA,countedA,isNull(countedB,0) as countedB,
(countedA-isNull(countedB,0))*100/CountedA as missed
FROM (SELECT search_query as actionTypeA, count(*) as countedA
FROM logs WHERE Action='A' GROUP BY actionType
) as TpA
LEFT JOIN
(SELECT search_query as actionTypeB, count(*) as countedB
FROM logs WHERE Action='B' GROUP BY actionType
) as TpB
ON TpA.ActionTypeA = TpB.ActiontypeB
The LEFT JOIN is required to select all activities (search_query) from the 'A' results, and join them to only those from the 'B' results where a B is available.
Since this is very basic SQL (and well optimized by SQL engines) I'd suggest to prevent WHERE EXISTS as much as possible. The IsNull() function is an MS-SQL function to force a NULL value into the int(0) value which can be used in a calculation.
Finally you could filter on
WHERE missed>0
to get the final result.

Custom aggregation in GROUP BY clause

If I have a table with a schema like this
table(Category, SubCategory1, SubCategory2, Status)
I would like to group by Category, SubCategory1 and aggregate the Status such that
if not all Status values over the group have a certain value Status will be 0 otherwise 1.
So my result set will look like
(Category, SubCategory1, Status)
I don't want to write a function. I would like to do it inside the query.
Assuming that status is a numeric data type, use:
SELECT t.category,
t.subcategory1,
CASE WHEN MIN(t.status) = MAX(t.status) THEN 1 ELSE 0 END AS status
FROM dbo.TABLE_1 t
GROUP BY t.category, t.subcategory1
You can test that both the minimum and maximum status for each group are equal to your desired value:
SELECT
category,
subcategory1,
CASE WHEN MIN(status) = 42 AND MAX(status) = 42 THEN 1 ELSE 0 END AS Status
FROM table1
GROUP BY category, subcategory1
Let's say you want to find groups that have all status values under 100
SELECT category, subcategory1,
CASE WHEN MAX(status) < 100 THEN 0 ELSE 1 END AS Status
FROM table1
GROUP BY category, subcategory1
All groups with status under 100 will have Status set to 0, and all groups with at least one status >= 100 will be set to 1.
I think that's what you're asking for, but if not let me know.
I would like to group by Category,
SubCategory1 and aggregate the Status
such that if not all Status values
over the group have a certain value
Status will be 0 otherwise 1.
I'm interpreting this as "If there exists a Status value in a given group not equal to a given parameter, the returned Status will be 0 otherwise 1".
Select T.Category, T.SubCategory1
, Case
When Exists(
Select 1
From Table As T2
Where T2.Category = T.Category
And T2.SubCategory1 = T.SubCategory1
And T2.Status <> #Param
) Then 0
Else 1
End As Status
From Table As T
Group By t.Category, T.SubCategory1
Something like that :
select
Category,
SubCategory1,
(
case
when good_record_count = all_record_count then 1
else 0
end
) as all_records_good
from (
select
t.Category,
t.SubCategory1,
sum( cast(coalesce(t.Status, 'GOOD', '1', '0') as int) ) good_record_count,
count(1) all_record_count
from
table_name t
group by
t.Category, t.SubCategory1
)