Group by clause with count() and where condition in subquery - sql

Table : Class
id Institute_id Name
------------------------------
1 1 MCAL - 1
2 1 MCAL - 2
3 1 BCA - 1
4 1 BCA - 2
Table : Groups
id Class_Id Institute_Id Name Status
--------------------------------------------------
1 1 1 PHP false
2 2 1 JAVA false
3 1 1 ORACLE false
4 2 1 LINUX true
5 2 1 ASP.NET false
6 3 1 OS false
7 4 2 CPP false
Expected Result :
id Name Count(*)
-----------------------------
1 MCAL - 1 2
2 MCAL - 2 2
3 BCA - 1 1
4 BCA - 2 0
Note: count only those records from Groups where Institute_Id = 1 and Status=false

Please try this
Select
c.id,
c.name,
SUM(case when g.class_id is NULL then 0 else 1 end) as count
from class c left join groups g
on c.id=g.class_id
and g.status='false'
and g.Institute_id=1
group by c.id,c.name
order by c.id
fiddle link: http://sqlfiddle.com/#!6/2b992/6

To count properly with outer joins, specify the column name instead of * in COUNT().
SELECT C.id, C.Name, [Count] = COUNT(G.Status)
FROM Class C
LEFT JOIN Groups G ON C.id = G.Class_id AND G.Institute_id = 1 AND G.Status = 'false'
GROUP BY C.id, C.Name
ORDER BY C.id

Related

postgresql how to check by two columns from join

There are some points and point_types with mark actual:
points
------
id point_type
------
1 1
2 1
3 2
point_types
------
id actual
------
1 true
2 false
Also there are directions:
directions
------
id point_from point_to
------
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
7 3 1
8 3 2
9 3 3
I need only directions with actual point_from and point_to:
id point_from point_to
------
1 1 1
2 1 2
4 2 1
5 2 2
Trying with:
SELECT d.id, d.point_from, d.point_to
FROM directions d
JOIN points p ON (d.point_from = p.id OR d.point_to = p.id)
JOIN point_types pt ON pt.id = p.TYPE AND pt.actual = TRUE
GROUP BY 1,2,3
but getting directions with actual point_from or actual point_to :
id point_from point_to
------
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
7 3 1
8 3 2
Use a CTE that returns all the ids of the not actual points and then with NOT EXISTS get only the directions that do not contain any of them:
WITH cte AS (
SELECT p.id
FROM points p INNER JOIN point_types pt
ON pt.id = p.point_type
WHERE pt.actual = false
)
SELECT d.*
FROM directions d
WHERE NOT EXISTS (SELECT * FROM cte c WHERE c.id IN (d.point_from, d.point_to));
See the demo.

How to add extra value to select query result

SubjectMaster
Id Subject
1 English
2 History
3 Maths
UserSubjectAssociation
Id Userid SubjectId
1 1 1
2 1 3
3 2 2
Logs
Id Userid SubjectId Examdate Percentage
1 1 1 02/20/2020 50
2 1 0 Null Null
3 2 1 02/20/2020 70
4 2 2 02/20/2020 60
5 3 0 Null Null
6 4 3 02/18/2020 56
These are my sample tables.
I want to show records from log table of zero as well as all assigned subject of user 1
Suppose user 1 has 2 subject 1 and 3.
Show records from logs where subjectid comes in 0 as well as 1,3
Required Output :
Logs
Id Userid SubjectId Examdate Percentage
1 1 1 02/20/2020 50
2 1 0 Null Null
3 2 1 02/20/2020 70
4 3 0 Null Null
5 4 3 02/18/2020 56
Query :
select * from logs where rdatetime >= '' and subjectid in (select id from subjectmaster where userid = 1)
'Or' did not work.It was giving wrong output.How to handle it.
If I understand correctly, you want a correlated subquery and condition like this:
select l.*
from logs l
where l.subjectid = 0 or
exists (select 1
from subjectmaster sm
where sm.userid = l.userid and
sm.subjectid = l.subjectid
);
You can do left join :
select l.*
from logs l left join
subjectmaster sm
on sm.userid = l.userid and
sm.subjectid = l.subjectid
where not (l.subjectid <> 0 and sm.subjectid is null);
This query will give you the desired result:
select *
from logs l
where subjectid = 0
OR
subjectid IN (select subjectid
from UserSubjectAssociation
where Userid = 1)

MS Access merge two tables

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;

Best way to by column and aggregation on another column

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;

Count occurrences of field values as they are displayed in order

thanks in advance for the help and sorry for how the "table" looks. Here's my question...
Let's say I have a subquery with this table (imagine the bold as column headers) as its output -
id 1 1 2 3 3 3 3 4 5 6 6 6
action o c o c c o c o o c c c
I would like my new query to output -
id 1 1 2 3 3 3 3 4 5 6 6 6
action o c o c c o c o o c c c
ct 1 2 1 1 2 3 4 1 1 1 2 3
#c 0 1 0 1 2 2 3 0 0 1 2 3
#o 1 1 1 0 0 1 1 1 1 0 0 0
where ct stands for count. Basically, I want to count (for each id) the occurrences of consecutive id and action as they happen. Let me know if this makes sense, and if not, how I can clarify my question.
Note: I realize the lag/lead functions may be helpful in this situation, along with the row_number() function. Looking for as many creative solutions as possible!
You are looking for the row_number() analytic function:
select id, action, row_number() over (partition by id order by id) as ct
from table t;
For #c and #o, you want cumulative sum:
select id, action, row_number() over (partition by id order by id) as ct,
sum(case when action = 'c' then 1 else 0 end) over
(partition by id order by <some column here>) as "#c",
sum(case when action = 'c' then 1 else 0 end) over
(partition by id order by <some column here>) as "#o"
from table t;
The one caveat is that you need a way to specify the order of the rows -- an id or date time stamp or something. SQL result sets and tables are inherently unordered, so there is no idea that one row comes before or after another.
SQL> select id, action,
2 row_number() over(partition by id order by rowid) ct,
3 sum(decode(action,'c',1,0)) over(partition by id order by rowid) c#,
4 sum(decode(action,'o',1,0)) over(partition by id order by rowid) o#
5 from t1
6 /
ID A CT C# O#
---------- - ---------- ---------- ----------
1 o 1 0 1
1 c 2 1 1
2 o 1 0 1
3 c 1 1 0
3 c 2 2 0
3 o 3 2 1
3 c 4 3 1
4 o 1 0 1
5 o 1 0 1
6 c 1 1 0
6 c 2 2 0
6 c 3 3 0
P.S. Sorry Gordon, didn't see your post.