Use Count function inside conditional part of Case Expression - sql

I have two tables with the following sample records in oracle database
1. staffs
inst_name name sid
ABC John 1
PQR Sam 2
ABC Tom 3
ABC Amit 4
PQR Jack 5
2. staffaccounts
sid account_no
1 4587
1 4588
2 4589
3 4581
3 4582
5 4583
5 4585
4 4586
Where I want the result like
inst_name account_type total
PQR SINGLE 1
ABC SINGLE 1
PQR DOUBLE 1
ABC DOUBLE 2
This can be achieved by a outer query, but I want to write a query where there is no outer query. Want to accomplish it in one single query.
SELECT
A .inst_name,
(
CASE COUNT (b.ac_no)
WHEN 1 THEN
'Single'
WHEN 2 THEN
'Double'
END
) account_type,
COUNT (A . NAME)
FROM
staffs A,
staffaccounts b
WHERE
A . s_id = b.s_id
GROUP BY
A .inst_name
The above query gives error ORA-00907: missing right parenthesis. Can it be done in single query or is outer query the only way out.
Oracle Version is 10g

May be something like this would work.
SELECT
A.inst_name,
CASE COUNT (b.account_no)
WHEN 1 THEN
'Single'
WHEN 2 THEN
'Double'
END account_type,
COUNT (A.name)
FROM
staffs A JOIN
staffaccounts b
ON
A.SID = b.sid
GROUP BY
A.inst_name , a.sid
ORDER BY 3;

You are grouping by inst_name, but this is not what you actually want, because you don't want a result row per inst_name, but per inst_name and account_type.
select
s.inst_name,
sa.account_type,
count(*) as total
from staffs s
join
(
select
sid,
case when count(*) = 1 then 'SINGLE' else 'DOUBLE' end as account_type
from staffaccounts
group by sid
having count(*) <= 2
) sa on sa.sid = s.sid
group by sa.account_type, s.inst_name
order by sa.account_type, s.inst_name;

You should learn how to properly use JOIN syntax. I prefer the explicit comparison syntax for CASE.
This may be what you want:
SELECT s.inst_name,
(CASE WHEN COUNT(sa.ac_no) = 1 THEN 'Single'
WHEN COUNT(sa.ac_no) = 2 THEN 'Double'
END) as account_type,
COUNT(*)
FROM staffs s JOIN
staffaccounts sa
ON s.SID = sa.sid
GROUP BY s.inst_name;
EDIT:
Now I see what you want:
SELECT s.inst_name,
(CASE WHEN cnt = 1 THEN 'Single'
WHEN cnt = 2 THEN 'Double'
END) as account_type,
COUNT(*)
FROM (SELECT s.*, COUNT(*) as cnt
FROM staffs s JOIN
staffaccounts sa
ON s.SID = sa.sid
GROUP BY s.id
) s
GROUP BY s.inst_name,
(CASE WHEN cnt = 1 THEN 'Single'
WHEN cnt = 2 THEN 'Double'
END);

I got only the way by using subquery but is in the easy way (more easier and readable) to achieve your requirement
SELECT inst_name, account_type, count(total) as total
FROM (
SELECT
a.inst_name,
CASE
WHEN COUNT (b.account_no) = 1 THEN 'Single'
WHEN COUNT (b.account_no) = 2 THEN 'Double'
END AS account_type,
COUNT (a.name) AS total
FROM staffs a
INNER JOIN staffaccounts b ON A . SID = b.sid
GROUP BY a.inst_name, a.sid) t GROUP BY inst_name, account_type
OUTPUT:
inst_name account_type total
ABC Double 2
PQR Double 1
ABC Single 1
PQR Single 1

Related

Find rows in which a column value only occurs once (Single-Sided Entries in Double Entry accounting system)

I have a table of bank transactions, AccountTransaction, and rows with for e.g.
Amount
Payee_Name
Transaction_ID
Is_Corresponding_Transaction
69.00
Bob Jones
1
1
-69.00
Bob Jones
1
0
25.00
Bill
2
1
-25.00
Bill
2
0
297.00
Sally
3
1
-5.00
Ted
4
1
2.50
Ted
4
0
2.50
Ted
4
0
How do I select only (all) TS like Sally's where the Transaction ID only occurs once?
Bonus points: How do I select TS like Ted's where the sum of all Is_Corresponding_Transaction = 0 != the sum of Is_Corresponding_Transaction = 1 for a given TS_ID?
I was looking and found a Group by or where not exists, but couldn't figure out how to get that to work
Here's an e.g. of what I tried:
select
Full_Name, amount, a.Posted_Date,a.Payee_Name, a.Memo, Accounts.Account_Name
from AccountTransaction a
left join Accounts on Accounts.Account_Code = a.Account_Code
left join users on a.UserId = users.UserId
where not exists (select 1 from AccountTransaction b where a.Transaction_ID = b.Transaction_ID having count(*)>1)
and a.Pending= 0
ORDER by a.Posted_Date desc
Just to expand on Stu's comment. Here is one option that uses the window function
with cte as (
Select *
,NetSum = sum(Amount) over (partition by Transaction_ID)
,NetCnt = sum(1) over (partition by Transaction_ID)
From YourTable
)
Select *
From cte
Where NetSum<>0
or NetCnt<>2

SQL - Using Count with WHERE clause

I'm trying to do a simple, at first sight, SQL query, but I can't wrap my head around what I'm doing wrong.
Problem
Imagine these are my tables:
TableUsers
UserID FirstName LastName
1 Bill Johnson
2 Alex Agnew
3 Mike Owen
4 Kate Ryan
TableArticles
ArticleID Description
1 Bananas
2 Eggs
3 Milk
TableOrders
OrderID UserID ArticleID
1 1 1
2 1 2
3 2 3
4 2 2
5 3 3
I want to list all users who have at least one order linked to their name, count how many orders and count how many of a specific article they ordered:
Desired result
UserID FirstName # Orders # Banana orders # Egg orders
1 Bill 2 1 1
2 Alex 2 0 1
3 Mike 1 0 0
I have tried:
SELECT
UserID,
FirstName,
COUNT(*) AS '# Orders',
(SELECT COUNT(*) FROM TableOrders O WHERE O.UserID = TableOrders.UserID AND O.ArticleID = 1) AS '# Banana orders',
(SELECT COUNT(*) FROM TableOrders O WHERE O.UserID = TableOrders.UserID AND O.ArticleID = 2) AS '# Egg orders',
FROM TableUsers
LEFT JOIN TableOrders ON TableOrders.UserID = TableUsers.UserID
GROUP BY UserID, FirstName
HAVING Count(*) > 0;
But I'm getting an error saying the ArticleID needs to be in an aggregate function or group by clause. If I add the ArticleID to the group by clause, users with multiple orders are shown more than once...
Any help is appreciated!
Thank you!
Just use conditional aggregation:
SELECT o.UserID, o.FirstName, COUNT(*) AS num_orders,
SUM(CASE WHEN o.ArticleID = 1 THEN 1 ELSE 0 END) as num_bananas,
SUM(CASE WHEN o.ArticleID = 2 THEN 1 ELSE 0 END) as num_eggs
FROM TableUsers u JOIN
TableOrders o
ON o.UserID = u.UserID
GROUP BY UserID, FirstName;
Notes:
You want users with orders, so just use an inner join, not an outer join.
The HAVING clause is then not necessary.
Table aliases make the query easier to read and write.
Don't use single quotes for column aliases. It is better to name the columns so they don't need to be escaped.

CASE expression sum - issue when joining to other tables

I have a case expression in a stored procedure summing an account field, and then inserting into a user id. The logic works... until joining to another table.
I tried adding distinct counts, and additional tables to the query, but still when I join to another table it applies the 1 value when I want it to be 0 to the account.
This is the calculation in the stored proc;
INSERT INTO #SUMMARY_TEMP (USER_ID,FSN_CNT )
(SELECT USER_ID,
SUM(CASE WHEN A_GROUP_CD = 'RED' AND A_TYPE_CD = 'FSN' THEN REC_COUNT ELSE 0 END)
) AS 'FSN_CNT',
FROM (SELECT A_ACCOUNT_NBR,
A_USER_ID,
A_GROUP_CD,
A_TYPE_CD,
COUNT(*) AS REC_COUNT
FROM EXCEPTION_DETAIL
INNER JOIN #STAFF ON A_REPORT_DT = #REPORT_DT
AND (A_USER = B_USER_ID)
GROUP BY A_ACCOUNT_NBR,
A_USER_ID_ID,
A_GROUP_CD,
A_TYPE_CD) EXCEPTIONS
GROUP BY A_USER_ID,
A_ACCOUNT_NBR)
This is the result which is what I expect for 2 USER Ids
A_ACCOUNT_NBR USER_ID FSN_CNT
123456 HENRY 0
123498 HENRY 1
374933 JOE 1
474930 JOE 0
but when I join to another table the data looks like
A_ACCOUNT_NBR USER_ID FSN_CNT
123456 HENRY 1
123498 HENRY 1
374933 JOE 1
474930 JOE 1
Its applying the 1 value to account 123456 & 474930 when it should be 0.
I think its because the other table does not have the ACCOUNT_NBR column - I am joining on USER_ID and so it applies the 1 to all ACCOUNT_NBR from table A.
Thanks for all the suggestions, I tried using a CTE, and the counts now look good, but its created duplicate rows as shown below. Any suggestions on how to remove the duplication, below is the join I am using for the CTE;
select cte.*, jt.USER_ID
from cte
join EXCEPTION_DETAIL jt on cte.USER_ID=jt.USER_ID
USER ACCOUNT_NBR FSN_CNT
HENRY 123456 0
HENRY 123456 0
HENRY 123498 1
HENRY 123498 1
JOE 374933 1
JOE 374933 1
JOE 474930 0
JOE 474930 0
you can separate the 1st query by using cte and join with it next level like below
with cte as
(
(SELECT USER_ID,
SUM(CASE WHEN A_GROUP_CD = 'RED' AND A_TYPE_CD = 'FSN' THEN REC_COUNT ELSE 0 END)
) AS 'FSN_CNT',
FROM (SELECT A_ACCOUNT_NBR,
A_USER_ID,
A_GROUP_CD,
A_TYPE_CD,
COUNT(*) AS REC_COUNT
FROM EXCEPTION_DETAIL
INNER JOIN #STAFF ON A_REPORT_DT = #REPORT_DT
AND (A_USER = B_USER_ID)
GROUP BY A_ACCOUNT_NBR,
A_USER_ID_ID,
A_GROUP_CD,
A_TYPE_CD) EXCEPTIONS
GROUP BY A_USER_ID,
A_ACCOUNT_NBR)
) select cte.*,jt.USER_ID from cte join jointable_name jt on cte.USER_ID=jt.USER_ID

Query to show one column multiplied

I have the following 3 tables;
Table_Names:
user_id Name
------------------
1 Mark
2 Tom
3 Ana
Table_Language:
language_id Language
-----------------------
1 English
2 German
Table_Name_Lang
id user_id language_id
---------------------------
1 1 1
2 1 2
3 2 1
4 3 2
How can I create a query to show the expected results like those below?
Name Expr_1_Eng Expr_1_Ger
---------------------------------
Mark English German
Tom English
Ana German
Thanks Tok
I would do:
select n.name,
max(case when l.language = 'English' then l.language end) as has_English,
max(case when l.language = 'German' then l.language end) as has_German
from names n join
name_lang nl
on nl.user_id = n.user_id join
lang l
on nl.language_id = l.language_id
group by n.name
You can try to use JOIN with condition aggregate function
SELECT Name,
MAX(CASE WHEN tnl.language_id = 1 then tl.Language end),
MAX(CASE WHEN tnl.language_id = 2 then tl.Language end)
FROM
Table_Name_Lang tnl
JOIN Table_Names tn on tnl.language_id = tn.language_id
JOIN Table_Language tl on tl.user_id = tnl.user_id
GROUP BY Name
I think you should use pivot:
select n.name,[0] as language_one,[1] as language_two,[2],[3] from
(select n.name,tl.language
tablename n
left join Table_Name_Lang tnl on n.userid=tnl.userid
left join table_language tl on tl.id=tnl.id
group by n.name,tl.language
)T
pivot
(
language
for name in [0],[1],[2],[3]
)AS PivotTable;

Get separate count for each condition within group

I am trying to get a view of table information from and Oracle 10g table that lists the counts of specific values of a column in their own columns with each row being the group value.
for example:
The first select is :
SELECT processed_by, count(priority) as P2
FROM agreement_activity
WHERE priority = '2'
GROUP BY processed_by
Which outputs:
PROCESSED_BY P2
------------------------------ ----------
Alicia 2
Christine 2
The second select is:
SELECT processed_by, count(priority) as P1
FROM agreement_activity
WHERE priority = '1'
GROUP BY processed_by
Which outputs:
PROCESSED_BY P1
------------------------------ ----------
Bonita 2
Alicia 6
Christine 2
What I am looking for is to output those values as the following:
PROCESSED_BY P1 P2
------------------------------ ---------- ----------
Bonita 2
Alicia 6 2
Christine 2 2
Is that possible?
You can use sum with the case expression to get conditional count:
select processed_by
, sum(case when priority = 1 then 1 else 0 end) as P1
, sum(case when priority = 2 then 1 else 0 end) as P2
from agreement_activity
group by processed_by
P.S. If you don't care that P1 or P2 maybe null instead of 0 you can omit else in both expressions.
this is how I implement the sql. I'm using firebird code but I think you can convert the code into your sql
SELECT
a.equipmentid,
a.name equipname,
w1.countwarranty1 ,
w2.countwarranty2
FROM TBL_EQUIPMENTMST a
inner JOIN
(select c.equipmentid, count(c.WARRANTYID) countwarranty1 from tbl_equipwarranty c where c.serviceproduct='1' group by c.equipmentid) w1
ON w1.equipmentid = a.equipmentid
inner JOIN
(select d.equipmentid, count(d.WARRANTYID) countwarranty2 from tbl_equipwarranty d where d.serviceproduct='2' group by d.equipmentid) w2
ON w2.equipmentid = a.equipmentid
inner JOIN
(select e.equipmentid, count(e.equiplocationid) countlocation from tbl_equiplocation e group by e.equipmentid) w3
ON w3.equipmentid = a.equipmentid
this is the output
My warranty table has only 2 warranty for equipment each that's why its only showing 2.
you can also inner join to the same table with different count.
as you can see my warranty has multiple warranty where serviceproduct is different on each table
if I edit your code it will be like this
SELECT a.processed_by, b.priority as p2, c.priority as p1
FROM agreement_activity a
inner join
(
SELECT w1.processed_by, count(w1.priority) as P2 FROM agreement_activity w1
WHERE w1.priority = '2' GROUP BY w1.processed_by
) b
on a.processed_by = b.processed_by
inner join
(
SELECT w2.processed_by, count(w2.priority) as P2 FROM w2.agreement_activity
WHERE w2.priority = '1' GROUP BY w2.processed_by
) c
on a.processed_by = c.processed_by
test it