Get null records in SQL - sql

I have the next query:
SELECT c.name as clientName, p.id as projectId, p.name as projectName, p.rate, u.name as userName, sum(w.duration) as workedHours
FROM Project p, User u, Worklog w, Client c
WHERE w.user_id = u.id AND w.project_id = p.id AND p.client_id = c.id
GROUP BY p.id, u.id
that returns the projects, clients, hourly rate and worked hours.
How should be changed to return also the projects where workedHours is equal with 0?
Because this query returns just the records where workedHours is not 0.
Thank you for your time.

The problem is that no row in worklog can be joined, and that your condition in the WHERE clause removes any row without worklog associated.
Solution 1 : Using a LEFT JOIN
Using a left join instead would solve your problem.
SELECT c.name as clientName, p.id as projectId, p.name as projectName, p.rate, u.name as userName, coalesce(sum(w.duration), 0) as workedHours
FROM Project p, User u, Client c
LEFT JOIN Worklog w ON w.project_id = p.id AND w.user_id = u.id
WHERE p.client_id = c.id
GROUP BY p.id, u.id
By the way your query is suspicious in other aspects. For example c.name is in the SELECT clause but not in the GROUP BY clause. I take it that you use MySQL which is the only RDBMS I'm aware of which allows such queries. You maybe should consider adding the retrieved columns in the GROUP BY clause.
Solution 2 : Using only ANSI JOINs
As underscore_d points out, you may want to avoid old-style joins completely, and preferable use the following query :
SELECT
c.name as clientName,
p.id as projectId,
p.name as projectName,
p.rate,
u.name as userName,
coalesce(sum(w.duration), 0) as workedHours
FROM Project p
CROSS JOIN User u
INNER JOIN Client c ON p.client_id = c.id
LEFT JOIN Worklog w ON w.project_id = p.id AND w.user_id = u.id
GROUP BY c.name, p.id, p.name, p.rate, u.id, u.name
Solution 3 - Using a subquery
Another solution is to use a subquery, which would allow you to remove the GROUP BY clause completely and get a more manageable query if you ever need to retrieve more information. I personally don't like long lists of columns in a GROUP BY clause.
SELECT
c.name as clientName,
p.id as projectId,
p.name as projectName,
p.rate,
u.name as userName,
(SELECT SUM(duration) FROM Worklog WHERE project_id = c.id AND user_id = u.id) as workedHours
FROM Project p
CROSS JOIN User u
INNER JOIN Client c ON p.client_id = p.id

You should use standard ANSI joins and use LEFT JOIN on worklog table and ultimately you have to use LEFT JOIN on the user table as follows:
SELECT C.NAME AS CLIENTNAME,
P.ID AS PROJECTID,
P.NAME AS PROJECTNAME,
P.RATE,
U.NAME AS USERNAME,
SUM(W.DURATION) AS WORKEDHOURS
FROM PROJECT P
JOIN CLIENT C
ON P.CLIENT_ID = C.ID
LEFT JOIN WORKLOG W
ON W.PROJECT_ID = P.ID
LEFT JOIN USER U
ON W.USER_ID = U.ID
GROUP BY P.ID,
U.ID;

Related

SQL to HiveQL conversion

I have this SQL query and I am trying to convert it so that it can be run on HiveQL 2.1.1.
SELECT p.id FROM page p, comments c, users u,
WHERE c.commentid= p.id
AND u.id = p.creatorid
AND u.upvotes IN (
SELECT MAX(upvotes)
FROM users u WHERE u.date > p.date
)
AND EXISTS (
SELECT 1 FROM links l WHERE l.relid > p.id
)
This does not work on Hive QL, as it has more than 1 SubQuery (which is not supported)
EXISTS or IN replacements from SQL to Hive SQL are done like this:
WHERE A.aid IN (SELECT bid FROM B...)
can be replaced by:
A LEFT SEMI JOIN B ON aid=bid
But I can`t come up with a way to do this with the additional MAX() function.
Use standard join syntax instead of comma separated :
SELECT p.id
FROM page p INNER JOIN
comments c
ON c.commentid= p.id INNER JOIN
users u
ON u.id = p.creatorid INNER JOIN
links l
ON l.relid > p.id
WHERE u.upvotes IN (SELECT MAX(upvotes)
FROM users u
WHERE u.date > p.date
);
I am not sure what the upvotes logic is supposed to be doing. The links logic is easy to handle. Hive may handle this:
SELECT p.id
FROM page p JOIN
comments c
ON c.commentid = p.id JOIN
users u
ON u.id = p.creatorid CROSS JOIN
(SELECT MAX(l.relid) as max_relid
FROM links l
) l
WHERE l.max_relid > p.id AND
u.upvotes IN (SELECT MAX(upvotes)
FROM users u
WHERE u.date > p.date
);

Group by and aggregate by multiple columns

Example tables
taccount
tuser
tproject
What I want to achieve:
accountName count(u.id) count(p.id)
-----------------------------------
Account A 1 1
Account B 1 1
Account C 2 3
In other words I want a single query to join these tables together and count user's and project's per account
I tried:
SELECT
a.name as "accountName",
count(u.name),
count(p.id)
FROM "taccount" a
INNER JOIN "tuser" u ON u.account_id = a.id
INNER JOIN "tproject" p ON p.admin_id = u.id
GROUP BY u.name, a.name, p.id
But it's not grouping by account. It's giving me the following result
Any advice?
You can try below
SELECT
a.name as "accountName",
count(distinct u.name),
count(p.id)
FROM "taccount" a
INNER JOIN "tuser" u ON u.account_id = a.id
INNER JOIN "tproject" p ON p.admin_id = u.id
GROUP BY a.name
When you do Aggregate Function and If there are Column are not do Aggregate you must put in your Group By, because Aggregate functions perform a calculation on a set of rows and return a single row.
SELECT
a.name as "accountName",
count(distinct u.name),
count(p.id)
FROM
"taccount" a
INNER JOIN "tuser" u ON u.account_id = a.id
INNER JOIN "tproject" p ON p.admin_id = u.id
GROUP BY
a.name
So you need just Group By your column "accountName"
change your group by column name
SELECT
a.name as "accountName",
count(distinct u.account_id),
count(p.id)
FROM "taccount" a
INNER JOIN "tuser" u ON u.account_id = a.id
INNER JOIN "tproject" p ON p.admin_id = u.id
GROUP BY a.name
this will work:
select a.name,count(u.id),count(p.id) from
taccount a,tuser b, tproject where
a.id=b.account_id and
b.id=c.admin_id
group by a.name;

SQL Get Only Most Recent Record for A User [duplicate]

This question already has answers here:
Get top 1 row of each group
(19 answers)
Closed 4 years ago.
I have a situation where I need to write a sql query to get all of the most recent responses for a student in a classroom. I basically want to show just their most recent response, not all of their responses. I have the query to get all of the responses and order them, however I can't figure out the part where it only grabs that user's most recent record.
Below is the query I have to this point. You can see from the sample data in the image it is pulling back all responses. What I basically want is either just the most recent for a particular student OR possibly just showing the max attempt for a particular Lesson/Page number combo. I have tried playing around with partition and group bys but I haven't found the right combination yet.
SELECT U.UserName, C.Name AS 'ClassroomName', U.FirstName, U.LastName, L.Name AS 'LessonName', P.PageNumber, R.Attempt, R.Created
FROM Responses R
INNER JOIN ClassroomUsers CU ON CU.UserId = R.UserId
INNER JOIN Classrooms C ON C.Id = CU.ClassroomId
INNER JOIN Questions Q ON Q.Id = R.QuestionId
INNER JOIN Pages P ON P.Id = Q.PageId
INNER JOIN Lessons L ON L.Id = P.LessonId
INNER JOIN AspNetUsers U ON U.Id = CU.UserId
WHERE CU.ClassroomId IN (
SELECT CU.ClassroomId
FROM ClassroomUsers CU
WHERE CU.UserId = #UserId
)
ORDER BY R.Created DESC
My favorite way of doing this is using Row_Number() which will number each row based upon the criteria you set - In your case, you'd partition by U.UserName since you want one row returned for each user and order by R.Created DESC to get the latest one.
That being the case, you'd only want to get back the rows that have RN=1, so you query that out as follows:
WITH cte AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY U.UserName ORDER BY R.Created DESC) AS RN
,U.UserName, C.Name AS 'ClassroomName', U.FirstName, U.LastName, L.Name AS 'LessonName', P.PageNumber, R.Attempt, R.Created
FROM Responses R
INNER JOIN ClassroomUsers CU ON CU.UserId = R.UserId
INNER JOIN Classrooms C ON C.Id = CU.ClassroomId
INNER JOIN Questions Q ON Q.Id = R.QuestionId
INNER JOIN Pages P ON P.Id = Q.PageId
INNER JOIN Lessons L ON L.Id = P.LessonId
INNER JOIN AspNetUsers U ON U.Id = CU.UserId
WHERE CU.ClassroomId IN (
SELECT CU.ClassroomId
FROM ClassroomUsers CU
WHERE CU.UserId = #UserId
)
)
SELECT * FROM cte WHERE RN = 1
Hope that makes sense / helps!!
Just another option is using the WITH TIES clause.
Example
SELECT top 1 with ties
U.UserName
, C.Name AS 'ClassroomName'
, U.FirstName
, U.LastName
, L.Name AS 'LessonName'
, P.PageNumber
, R.Attempt
, R.Created
FROM Responses R
INNER JOIN ClassroomUsers CU ON CU.UserId = R.UserId
INNER JOIN Classrooms C ON C.Id = CU.ClassroomId
INNER JOIN Questions Q ON Q.Id = R.QuestionId
INNER JOIN Pages P ON P.Id = Q.PageId
INNER JOIN Lessons L ON L.Id = P.LessonId
INNER JOIN AspNetUsers U ON U.Id = CU.UserId
WHERE CU.ClassroomId IN (
SELECT CU.ClassroomId
FROM ClassroomUsers CU
WHERE CU.UserId = #UserId
)
Order By ROW_NUMBER() OVER(PARTITION BY U.UserName ORDER BY R.Created DESC)

Check Specfic row with multiple rows in ORACLE

I want to select users list who is status is disabled for all application in username(ur_username table and that users should be active in Person Table(ur_person).
I tried with the following query :
select distinct E.PERSON_ID , E.DOMAIN_ID,e.first_name,e.last_name,e.type,E.username, E.SYSTEM_name from
(SELECT distinct p.person_id,p.domain_id,p.first_name,p.last_name,p.type,u.username, U.STATUS, U.SYSTEM_ID,s.system_name from ur_username u join
ur_username_person up on u.username_id=up.username_id
join ur_person p on up.person_id=p.person_id join ur_system s on u.system_id=s.system_id
WHERE p.status='ACTIVE') E WHERE E.person_id IN
( select distinct P.PERSON_ID from ur_username u join ur_username_person up on u.username_id=up.username_id
join ur_person p on up.person_id=p.person_id
where u.status='DISABLED')
I need to get the person list who is status is Disabled in all system in username Table as below image:
But instead I am getting person who is disable in one system but active in some other systems also:
You can get the person_ids using aggregation and having:
select p.person_id
from ur_username u join
ur_username_person up
on u.username_id = up.username_id join
ur_person p join
ur_system s
on u.system_id = s.system_id
where p.status = 'ACTIVE'
group by p.person_id
having max(u.status) = min(u.status) and max(u.status) ='DISABLED';
You specify that you want the persons, so this seems to answer your question. You can easily enhance this query to return more columns.

Creating a stored procedure with many-to-many relationship giving proper response

I'm trying to create a Stored Procedure for my school project which uses Model First.
I wanted to make a SP for returning av list of games with most orders, as a 'top list' so to speak, but can't figure it out after searching for similiar threads. The parameter #antal should return the range of distinct results to give back. So let's say I send in 5 it should return 5 Products, and 3 should return 3 products and so forth...
Since I am new to this, I'm stuck. The code so far is:
use SpelAffarenDatabas
go
create procedure [dbo].[GetTopListGames]
(
#antal int
)
as
select distinct top (6) p.Id, p.Name, p.Orders, k.Name, g.Name from ProduktSet as p
left join ConsoleProduct as kp
on p.Id = kp.Product_Id
left join ConsoleSet as k
on kp.Console_Id = k.Id
left join ProductGenre as pg
on p.Id = pg.Product_Id
left join GenreSet as g
on pg.Genre_Id = g.Id
group by p.Id, p.Name, p.Orders, k.Name, g.Name
So how do I go about getting a proper response that gives me a proper response, whic I guess would be of distinct entities?
select distinct top (#antal) p.Id, p.Name, p.Orders, k.Name, g.Name from ProduktSet as p
left join ConsoleProduct as kp
on p.Id = kp.Product_Id
left join ConsoleSet as k
on kp.Console_Id = k.Id
left join ProductGenre as pg
on p.Id = pg.Product_Id
left join GenreSet as g
on pg.Genre_Id = g.Id
group by p.Id, p.Name, p.Orders, k.Name, g.Name
order by p.Orders Desc