Record filtration issue - sql

I have 2 tables in database : 1. Agents table & 2. UserPermissions table
Table 1 Agents contains list of Agents as given below; here CompanyID & AgentID are unique values.
AgentID AgentCode CompanyID RailEnabled
1 A1 1 1
2 A2 2 0
3 A3 3 1
4 A4 4 0
Table 2 UserPermissions table contains list of all users who are coming under a particular agent. And there are 2 fields ModuleCode whose values are like 'RAL' for Rail in short and Enabled fields.
UserID UserName ModuleCode Enabled CompanyID
1 U1 RAL 1 1
2 U2 RAL 0 1
3 U3 RAL 1 1
13 U4 BUS 1 1
4 U1 RAL 0 2
5 U2 RAL 0 2
6 U3 RAL 0 2
14 U4 HTL 1 2
7 U1 RAL 0 3
8 U2 RAL 0 3
9 U3 RAL 0 3
15 U4 FLT 1 3
10 U1 RAL 0 4
11 U2 RAL 0 4
12 U3 RAL 0 4
16 U4 BUS 1 4
Now I need to filter out only the Agents for which RailEnabled is true but none of the users of that Agent is not enabled for Rail service(Module code: RAL).
Thanks for your help in advance.

Use NOT EXISTS.
SQLFiddle
select * from agents a
where railenabled = 1 --assuming 1 means enabled
and not exists (select 1 --not exists to remove all agents whose users have RAL enabled
from userpermissions b
where a.companyid = b.companyid
and b.modulecode = 'RAL'
and b.enabled = 1
);

Related

Aggregate features row-wise in dataframe

i am trying to create features from sample that looks like this:
index
user
product
sub_product
status
0
u1
p1
sp1
NA
1
u1
p1
sp2
NA
2
u1
p1
sp3
CANCELED
3
u1
p1
sp4
AVAIL
4
u2
p3
sp2
AVAIL
5
u2
p3
sp3
CANCELED
6
u2
p3
sp7
NA
first, i created dummies:
pd.get_dummies(x, columns = ['product', 'sub_product', 'status']
but i also need to group by row, to have 1 row by user, what is the best way to do it?
If i'll just group it:
pd.get_dummies(x, columns = ['product', 'sub_product', 'status'].groupby('user').max()
user
product_p1
product_p3
sub_product_sp1
sub_product_sp2
sub_product_sp3
sub_product_sp4
sub_product_sp7
status_AVAIL
status_CANCELED
status_NA
u1
1
0
1
1
1
1
0
1
1
1
u2
0
1
0
1
1
0
1
1
1
1
i will loose information, fo ex. that for u1 sp3 status is canceled. So it's looks like i have to create dummies for every column combination?
Update: You are basically looking for pivot:
out = (df.astype(str)
.assign(value=1)
.pivot_table(index=['user'], columns=['product','sub_product','status'],
values='value', fill_value=0, aggfunc='max')
)
out.columns = ['_'.join(x) for x in out.columns]

SQL SELECT items With Different Categories

Here is the table:
itemcode
batch
subbatch
desc
itemcode
batch
subbatch
desc
A
1
1
red
B
1
1
red
C
9
1
red
C
9
2
blue
D
8
1
red
D
8
2
blue
E
2
1
red
F
3
1
red
G
4
1
red
H
1
1
red
I
9
1
red
I
9
2
blue
J
8
1
red
J
8
2
blue
There are two kinds of items, one item with just 1 batch and 1 subbatch, the other item has 1 batch and multiple subbatch.
I want to select all items with the 1 batch / 1 subbatch including the other items with 1 batch and a specific subbatch. In this case, items with batch 8 or 9 has multiple subbatch. I wanted to select all items that is not in batch 8 or 9 plus items with batch 8 or 9 with subbatch 2.
From what you describe you can use not exists with boolean logic:
select t.*
from t
where not exists (select 1
from t t2
where t2.itemcode = t.itemcode and
t2.subbatch <> t.subbatch
) or
subbatch = 2;

Row Act like Column Database Query

I have a Person table.Persons are good at some language and i give them weight.My table are as follows:
Person
ID Name Description
1 Rodra Some..
2 Rakib Some..
3 Samsad Some..
4 Foysal Some..
Language
TypeID TypeName
1 C#
2 Asp.Net
3 Python
4 JSP
5 Java
6 Jquery
7 Android
PersonSkill
ID PersonID TypeID Weight
1 1 1 60
2 1 3 50
3 1 7 40
4 2 1 80
5 2 2 70
6 3 1 90
7 3 2 50
8 4 1 60
9 4 2 50
10 4 6 40
11 4 7 55
Now i want to query those person who know c#(TypeId 1)>65 and Asp.net(TypeID 2)>65.How to do it?Anyone helps me greatly appreciated.
Select *
FROM person
WHERE EXISTS(SELECT * FROM PersonSkill WHERE PersonId = Person.Id AND TypeID = 1 AND Weight>65)
AND EXISTS(SELECT * FROM PersonSkill WHERE PersonId = Person.Id AND TypeID = 2 AND Weight>65)
select p.* from
person p
JOIN personSkill ps ON p.ID=ps.PersonID
JOIN Language l ON l.TypeID=ps.TypeID
WHERE (l.TypeName = 'c#' AND ps.weight>65)
AND
(l.TypeName = 'Asp.net' AND ps.weight>65)

select max id of each id

I have two tables: tbComment and tbCommentStatus:
**tbComment**
CommentID IsLocked
1 0
2 0
3 1
4 0
5 1
**tbCommentStatus**
CommentStatusID CommentID StatusTypeID
105 1 1
106 1 4
107 2 1
108 3 1
109 3 4
110 4 1
112 5 1
112 5 4
I want to return CommentIDs of a dataset of the highest CommentStatusIDs for each CommentID Where StatusTypeID = 4 and IsLocked = 1.
Basically, here I would return CommentIDs: 3,5 because their highest CommentStatusID has a StatusTypeID=4 and tbComment.IsLocked=1
Man, I hope this makes sense. If it doesn't I can clarify.
Thanks!
Try the following query.
SELECT c.CommentID, MAX(cs.CommentStatusID) MaxCommentStatusID
FROM tbComment c
JOIN tbCommentStatus cs ON c.CommentID = cs.CommentID
WHERE c.IsLocked = 1
AND cs.StatusTypeID = 4
GROUP BY c.CommentID

SQL for MS Access: Another question about COUNT, JOIN, 0s and Dates

I asked a question regarding joins yesterday. However although that answer my initial question, i'm having more problems.
I have a telephony table
ID | Date | Grade
1 07/19/2010 Grade 1
2 07/19/2010 Grade 1
3 07/20/2010 Grade 1
4 07/20/2010 Grade 2
5 07/21/2010 Grade 3
I also have a Grade table
ID | Name
1 Grade 1
2 Grade 2
3 Grade 3
4 Grade 4
5 Grade 5
6 Grade 6
7 Grade 7
8 Grade 8
9 Grade 9
10 Grade 10
11 Grade 11
12 Grade 12
I use the following query to get the COUNT of every grade in the telephony table, it works great.
SELECT grade.ID, Count(telephony.Grade) AS Total
FROM grade LEFT JOIN telephony ON grade.ID=telephony.Grade
GROUP BY grade.ID
ORDER BY 1;
This returns
ID | Total
1 3
2 1
3 1
4 0
5 0
6 0
7 0
8 0
9 0
10 0
11 0
12 0
However, what i'm trying to do is the following:
Group by date and only return results between two dates
SELECT telephony.Date, grade.ID, Count(telephony.Grade) AS Total
FROM grade LEFT JOIN telephony ON grade.ID=telephony.Grade
WHERE telephony.Date BETWEEN #07/19/2010# AND #07/23/2010#
GROUP BY telephony.Date, grade.ID
ORDER BY 1;
I'm getting the following
Date | ID | Total
07/19/2010 1 2
07/20/2010 1 1
07/20/2010 2 1
07/21/2010 3 1
It's not returning all the grades with 0 entries between the two dates, only the entries that exist for those dates. What i'm looking for is something like this:
Date | ID | Total
07/19/2010 1 2
07/19/2010 2 0
07/19/2010 3 0
07/19/2010 4 0
07/19/2010 5 0
07/19/2010 6 0
07/19/2010 7 0
07/19/2010 8 0
07/19/2010 9 0
07/19/2010 10 0
07/19/2010 11 0
07/19/2010 12 0
07/20/2010 1 1
07/20/2010 2 1
07/20/2010 3 0
07/20/2010 4 0
07/20/2010 5 0
07/20/2010 6 0
07/20/2010 7 0
07/20/2010 8 0
07/20/2010 9 0
07/20/2010 10 0
07/20/2010 11 0
07/20/2010 12 0
07/21/2010 1 2
07/21/2010 2 0
07/21/2010 3 1
07/21/2010 4 0
07/21/2010 5 0
07/21/2010 6 0
07/21/2010 7 0
07/21/2010 8 0
07/21/2010 9 0
07/21/2010 10 0
07/21/2010 11 0
07/21/2010 12 0
07/22/2010 1 2
07/22/2010 2 0
07/22/2010 3 0
07/22/2010 4 0
07/22/2010 5 0
07/22/2010 6 0
07/22/2010 7 0
07/22/2010 8 0
07/22/2010 9 0
07/22/2010 10 0
07/22/2010 11 0
07/22/2010 12 0
07/23/2010 1 2
07/23/2010 2 0
07/23/2010 3 0
07/23/2010 4 0
07/23/2010 5 0
07/23/2010 6 0
07/23/2010 7 0
07/23/2010 8 0
07/23/2010 9 0
07/23/2010 10 0
07/23/2010 11 0
07/23/2010 12 0
I hope someone can help. I'm using Microsoft Access 2003.
Cheers
Create a separate query on telephony which uses your BETWEEN #07/19/2010# AND #07/23/2010# constraint.
qryTelephonyDateRange:
SELECT *
FROM telephony
WHERE [Date] BETWEEN #07/19/2010# AND #07/23/2010#;
Then, in your original query, use:
LEFT JOIN qryTelephonyDateRange ON grade.ID=qryTelephonyDateRange.Grade
instead of
LEFT JOIN telephony ON grade.ID=telephony.Grade
You could use a subquery instead of a separate named query for qryTelephonyDateRange.
Note Date is a reserved word, so I bracketed the name to avoid ambiguity ... Access' database engine will understand it is supposed to be looking for a field named Date instead of the VBA Date() function. However, if it were my project, I would rename the field to avoid ambiguity ... name it something like tDate.
Update: You asked to see a subquery approach. Try this:
SELECT g.ID, t.[Date], Count(t.Grade) AS Total
FROM
grade AS g
LEFT JOIN (
SELECT Grade, [Date]
FROM telephony
WHERE [Date] BETWEEN #07/19/2010# AND #07/23/2010#
) AS t
ON g.ID=t.Grade
GROUP BY g.ID, t.[Date]
ORDER BY 1, 2;
Try this:
SELECT grade.ID, Count(telephony.Grade) AS Total
FROM grade LEFT JOIN telephony ON grade.ID=telephony.Grade
GROUP BY grade.ID
HAVING COUNT(telephony.Grade) > 0
ORDER BY grade.ID;
That's completely different.
You want a range of individual dates joined with your first table, and the between clause isn't going to do that for you.
I think you'll need a table with all the dates you want, say from 1/1/2010 to 12/31/2010, or whatever range you need to support. One column, 365 or however many rows with one date value each.
then join that table with the ones with the dates and grades, and limit by your date range,
then do the aggregation to count.
Take it one step at a time and it will be easier to figure out.
The way I got it to work was to:
Create a table named Dates with a single primary key date/time field named MyDate (I'm with HansUp on not using reserved words like "Date" for field names).
Fill the table with the date values I wanted (7/19/2010 to 7/23/2010, as in your example).
Write a query with the following SQL statement
SELECT x.MyDate AS [Date], x.ID, Count(t.ID) AS Total
FROM (SELECT Dates.MyDate, Grade.ID FROM Dates, Grade) AS x
LEFT JOIN Telephony AS t ON (x.MyDate = t.Date) AND (x.ID = t.Grade)
GROUP BY x.MyDate, x.ID;
That should get the results you asked for.
The subquery statement in the SQL creates a cross-join to get you every combination of date in the Dates table and grade in the Grade table.
(SELECT Dates.MyDate, Grade.ID FROM Dates, Grade) AS x
Once you have that, then it's just an outer join to the Telephony table to do the rest.