I have 2 tables whose structure is as follows:
tblBookMst
Id bk_isbn bk_title bk_author
----------------------------------------------------
1 ISBN_001 ABC Book AA
2 ISBN_002 DEF Book BB
3 ISBN_003 GHI Book CC
4 ISBN_004 JKL Book DD
and
tblBookId
b_id id lib_id inv_stat
----------------------------------------------------
1 1 BK/LIB/01 1
2 1 BK/LIB/02 2
3 1 BK/LIB/03 2
4 2 BK/LIB/04 1
5 2 BK/LIB/05 1
6 3 BK/LIB/06 1
('inv_stat' legends: 1=> In Stock & 2 => In Circulation)
Using the above 2 tables, i want to write a query which will give me output as shown below
bk_title bk_author tot_copies in_stock in_circulation
ABC Book AA 3 1 2
DEF Book BB 2 2 0
GHI Book CC 1 1 0
Till now i have been unable to figure out how to calculate the 'in_stock' & 'in_circulation'.I am using the below mentioned sql query.
SELECT a.id,a.bk_title,a.bk_author,count(b.lib_id) as tot_copies
FROM tblBookMst a
JOIN tblBookId b ON a.id = b.id
GROUP BY a.id,a.bk_title,a.bk_author
ORDER BY a.bk_title
I hope you understand my question.Please advice with example
You are close! You just need some Case Statements:
SELECT a.id,
a.bk_title,
a.bk_author,
count(b.lib_id) as tot_copies
SUM(CASE WHEN b.inv_stat = 1 THEN 1 ELSE 0 END) as in_stock,
SUM(CASE WHEN b.inv_stat = 2 THEN 1 ELSE 0 END) as in_circulation
FROM tblBookMst a
JOIN tblBookId b ON a.id = b.id
GROUP BY a.id,a.bk_title,a.bk_author
ORDER BY a.bk_title
Related
I have a table that looks something like this:
id name status
2 a 1
2 a 2
2 a 3
2 a 2
2 a 1
3 b 2
3 b 1
3 b 2
3 b 1
and the resultant i want is:
id name total count count(status3) count(status2) count(status1)
2 a 5 1 2 2
3 b 4 0 2 2
please help me get this result somehow, i can just get id, name or one of them at a time, don't know how to put a clause to get this table at once.
Here's a simple solution using group by and case when.
select id
,count(*) as 'total count'
,count(case status when 3 then 1 end) as 'count(status1)'
,count(case status when 2 then 1 end) as 'count(status3)'
,count(case status when 1 then 1 end) as 'count(status2)'
from t
group by id
id
total count
count(status3)
count(status2)
count(status1)
2
5
1
2
2
3
4
0
2
2
Fiddle
Here's a way to solve it using pivot.
select *
from (select status,id, count(*) over (partition by id) as "total count" from t) tmp
pivot (count(status) for status in ([1],[2],[3])) pvt
d
total count
1
2
3
3
4
2
2
0
2
5
2
2
1
Fiddle
I had two tables as below:
request_id
update_from_id
sw_ref_number
raised_by_user_id
raised_date
1
0
1
3
2019-08-29 15:08:16.000
id
request_id
input_id
value
is_deleted
21
1
1
00001
0
22
1
2
3
0
75
2
1
00002
0
76
2
2
0
My query is:
select req.request_id,
req.sw_ref_number,
reqDet.[value] ,
reqDet.input_id
FROM SOF.tblSOFRequest req
left join SOF.tblSOFRequestDetails reqDet
on req.request_id = reqDet.request_id
where reqDet.input_id = 1
or reqDet.input_id = 2
And after that my result is:
request_id
sw_ref_number
value
input_id
1
1
00001
1
1
1
3
2
2
2
00002
1
2
2
2
I want a result as:
request_id
sw_ref_number
epi_db_no
manuf_no
1
1
00001
3
2
2
00002
Here input_id = 1 means epi_db_no and input_id = 2 means manuf_no.
How can I get this?
Thanks
You can do it using self-join like the following query.
SELECT req.request_id
,req.sw_ref_number
,reqDet.[value] AS epi_db_no
,reqDet2.[value] AS manuf_no
FROM SOF.tblSOFRequest req
LEFT JOIN SOF.tblSOFRequestDetails reqDet ON req.request_id = reqDet.request_id
LEFT JOIN SOF.tblSOFRequestDetails reqDet2 ON reqDet.request_id = reqDet2.request_id
AND reqDet2.input_id = 2
WHERE reqDet.input_id = 1
This seems like a simple pivot. I use conditional aggregation here:
SELECT req.request_id,
req.sw_ref_number,
MAX(CASE reqDet.input_id WHEN 1 THEN reqDet.[value] END) AS epi_db_no,
MAX(CASE reqDet.input_id WHEN 2 THEN reqDet.[value] END) AS manuf_no
FROM SOF.tblSOFRequest req
JOIN SOF.tblSOFRequestDetails reqDet ON req.request_id = reqDet.request_id
WHERE reqDet.input_id IN (1,2)
GROUP BY req.request_id,
req.sw_ref_number;
I need to create a new table base on two tables. I have a database in ms access and need to migrate.
tableT tableS
ID CustID DATE Exp1 Exp2 ID CustID DATE Tem1 Tem2
-------------------------------- ---------------------------------
1 1 1/1/00 5 5 1 1 1/1/00 3 4
2 2 1/1/00 1 3 2 2 1/1/00 5 0
3 1 3/1/00 3 2 3 1 5/1/00 0 3
4 3 4/1/00 4 1 4 3 6/1/00 0 0
Desired output table tableNew:
ID CustID DATE Exp1 Exp2 Tem1 Tem2
---------------------------------------------
1 1 1/1/00 5 5 3 4
2 2 1/1/00 1 3 5 0
3 1 3/1/00 3 2
4 3 4/1/00 4 1
5 1 5/1/00 0 3
6 3 6/1/00 0 0
If I use outer join, I will not get the output I need.
Any idea or help.
You want a full join. You can emulate this in MS Access using:
select t.CustID, t.DATE, t.Exp1, t.Exp2, s.tem1, s.tem2
from TableT as t left outer join
tableS as s
on t.CustId = s.CustId and t.date = s.date
union all
select s.CustID, s.DATE, null, null, s.tem1, s.tem2
from tableS as s left outer join
tableT as t
on t.CustId = s.CustId and t.date = s.date
where t.CustId is null;
I want to create a rank column using existing rank and binary columns. Suppose for example a table with ID, RISK, CONTACT, DATE. The existing rank is RISK, say 1,2,3,NULL, with 3 being the highest. The binary-valued is CONTACT with 0,1 or FAILURE/SUCESS. I want to create a new RANK that will order by RISK once a certain number of successful contacts has been exceeded.
For example, suppose the constraint is a minimum of 2 successful contacts. Then the rank should be created as follows in the two instances below:
Instance 1. Three ID, all have a min of two successful contacts. In that case the rank mirrors the risk:
ID risk contact date rank
1 3 S 1 3
1 3 S 2 3
1 3 F 3 3
1 3 F 4 3
2 2 S 1 2
2 2 S 2 2
2 2 F 3 2
2 2 F 4 2
3 1 S 1 1
3 1 S 2 1
3 1 S 3 1
Instance 2. Suppose ID=1 has only one successful contact. In that case it is relegated to the lowest rank, rank=1, while ID=2 gets the highest value, rank=3, and ID=3 maps to rank=2 because it satisfies the constraint but has a lower risk value than ID=2:
ID risk contact date rank
1 3 S 1 1
1 3 F 2 1
1 3 F 3 1
1 3 F 4 1
2 2 S 1 3
2 2 S 2 3
2 2 F 3 3
2 2 F 4 3
3 1 S 1 2
3 1 S 2 2
3 1 S 3 2
This is SQL, specifically Hive. Thanks in advance.
Edit - I think Gordon Linoff's code does it correctly. In the end, I used three interim tables. The code looks like that:
First,
--numerize risk, contact
select A.* ,
case when A.risk = 'H' then 3
when A.risk = 'M' then 2
when A.risk = 'L' then 1
when A.risk is NULL then NULL
when A.risk = 'NULL' then NULL
else -999 end as RISK_RANK,
case when A.contact = 'Successful' then 1
else NULL end as success
Second,
-- sum_successes_by_risk
select A.* ,
B.sum_successes_by_risk
from T as A
inner join
(select A.person, A.program, A.risk, sum(a.success) as sum_successes_by_risk
from T as A
group by A.person, A.program, A.risk
) as B
on A.program = B.program
and A.person = B.person
and A.risk = B.risk
Third,
--Create table that contains only max risk category
select A.* ,
B.max_risk_rank
from T as A
inner join
(select A.person, max(A.risk_rank) as max_risk_rank
from T as A
group by A.person
) as B
on A.person = B.person
and A.risk_rank = B.max_risk_rank
This is hard to follow, but I think you just want window functions:
select t.*,
(case when sum(case when contact = 'S' then 1 else 0 end) over (partition by id) >= 2
then risk
else 1
end) as new_risk
from t;
I am building a report and I am stuck formulating a query. I am bringing the following data from multiple tables after a lot of joins.
ID TYPE RATING
----- ---- ------
ID_R1 A 1
ID_R1 B 3
ID_R2 A 2
ID_R2 B 1
ID_R3 A 4
ID_R3 B 4
ID_R4 A 2
ID_R4 B 3
ID_R5 A 2
ID_R5 B 3
What actually is happening is that Every ID will have a Rating for Type A & B so what I need to do is transform the above into the following
ID Type_A_Rating Type_B_Rating
----- ------------- -------------
ID_R1 1 3
ID_R2 3 1
ID_R3 4 4
ID_R4 2 3
ID_R5 2 3
I have think group by and different techniques but so far I am unable to come up with a solution. Need help F1! F1!
p.s just for the record my end game is getting the count of (A,B) combinations
Type_A_Rating Type_B_Rating Count
------------- ------------- -----
1 1 0
1 2 0
1 3 1
1 4 0
2 1 0
2 2 0
2 3 2
2 4 0
3 1 1
3 2 0
3 3 0
3 4 0
4 1 0
4 2 0
4 3 0
4 4 1
From this you can see that a simple GROUP BY with any form AND OR conditions doesn't suffice until I get the data as mentioned. I could use two intermediate/temp tables, in one get Type_A_Rating with ID and then in second Type_B_Rating with ID and then in another combine both but isn't there a better way.
This should work as SQL engine agnostic solution (provided that there is exactly one row with type A for each ID and one row with type B for each ID):
select
TA.ID,
TA.RATING as Type_A_Rating,
TB.RATING as Type_B_Rating
from
(select ID, RATING
from T where TYPE = 'A') as TA
inner join
(select ID, RATING
from T where TYPE = 'B') as TB
on TA.ID = TB.ID
Related SQL Fiddle: http://sqlfiddle.com/#!9/7e6fd9/2
Alternative (simpler) solution:
select
ID,
sum(case when TYPE = 'A' then RATING else 0 end) as Type_A_Rating,
sum(case when TYPE = 'B' then RATING else 0 end) as Type_B_Rating
from
T
group by
ID
Fiddle: http://sqlfiddle.com/#!9/7e6fd9/3
EDIT:
The above is correct but both can be simplified a bit:
select TA.ID, TA.RATING as Type_A_Rating, TB.RATING as Type_B_Rating
from T TA join
T TB
on TA.ID = TB.ID AND A.type = 'A' and B.type = 'B';
And (because I prefer NULL when there are no matches:
select ID,
max(case when TYPE = 'A' then RATING end) as Type_A_Rating,
max(case when TYPE = 'B' then RATING end) as Type_B_Rating
from T
group by ID