Okay, I'm not as SQL savvy as I should be, but I'm familiar with how Joins, Unions, etc... work and I just can't come up with a solution for this. I'm trying to get data from two different tables, but the monkey wrench is that the right side table may have more rows than I actually want in my result set. The only criteria I have to join the two tables is an email address. Here's the code:
CREATE TABLE #PPEInfo(StudentEmail nvarchar(128), StudentName nvarchar(128), DevUnits int)
INSERT INTO #PPEInfo (StudentEmail, DevUnits)
SELECT e.StudentEmail AS Email, sum(ck.DevelopmentUnits) AS DevUnits
FROM Enrollments e, CourseKeys ck
WHERE e.CertGenerated = 'true'
AND e.CourseId = ck.CourseId
GROUP BY e.StudentEmail
ORDER BY DevUnits DESC
SELECT p.StudentEmail, p.DevUnits, s.StudentName
FROM #PPEInfo p
RIGHT OUTER JOIN Surveys s
ON p.StudentEmail = s.StudentEmail
ORDER BY DevUnits DESC, StudentName ASC
DROP TABLE #PPEInfo
The problem is that I receive multiple student names because they may not have used the same one when doing their submissions. For instance:
Email Address James R. Salvati
Email Address James Salvati
The only solution that I've come up with is to populate my temp table with the email addresses first and then query the Surveys table for the name using "TOP(1)" to get only one student name. It does work, but it's very CPU intensive, and I'm dealing with a lot of records. Here's the code (although I didn't care about the DevUnits at this point was just trying to come up with something):
CREATE TABLE #PPEInfo(ID int IDENTITY(1,1), StudentEmail nvarchar(128), StudentName nvarchar(128), DevUnits int)
INSERT INTO #PPEInfo (StudentEmail)
SELECT DISTINCT StudentEmail FROM Enrollments
WHERE CertGenerated = 'true'
DECLARE #rowID int
DECLARE #email nvarchar(128)
SET #rowID = (SELECT max(ID) FROM #PPEInfo)
WHILE (#rowID > 0)
BEGIN
SET #email = (SELECT StudentEmail FROM #PPEInfo WHERE ID = #rowID)
UPDATE #PPEInfo
SET StudentName = (SELECT TOP(1) s.StudentName FROM Surveys s
WHERE s.StudentEmail = #email)
WHERE ID = #rowID
SET #rowID = #rowID - 1
END
SELECT * FROM #PPEInfo
ORDER BY DevUnits DESC
DROP TABLE #PPEInfo
I've never had to actually post on one of these boards. I usually find the solution or figure one out, but this one is just beyond my SQL prowess.
Thanks!!!
It depends on how you want to determine which name to select when there are multiple. One posible way is below:
SELECT p.StudentEmail, p.DevUnits, MAX(s.StudentName)
FROM #PPEInfo p
RIGHT OUTER JOIN Surveys s
ON p.StudentEmail = s.StudentEmail
ORDER BY DevUnits DESC, StudentName ASC
GROUP BY p.studentEmail, p.devUnits
Here you are grouping by email and units and grabbing the "MAX" student name.
Also in your first query you should stop using implicit joins.
Correct me if I'm wrong, but I'm pretty sure that you can do this to achieve what you want:
select * from enrollments
inner join coursekeys on
enrollments.studentemail = coursekeys.studentemail
where coursekeys.studentname=(
select top 1 studentname from coursekeys
where studentemail=enrollments.studentemail
);
I don't have access to a SQL Server at the moment, but I've succesfully achieved this on a MySQL server with similar tables to yours.
Related
Ok, so I have a table Assignment:
[UserId]
[GroupId]
[UpdatedBy]
[UpdatedAt]
Also, I have a function for returning users from a specific group:
select UserId
from dbo.GetGroupUsers() ggu
where ggu.GroupId = ?
In Assignment, I want to check all groups that our user is listed and then I want to select ALL users from these groups, without duplicate.
How can I achieve this?
Edit:
Sample output form selecting groupid = 4
for example user "test1" belong to other group where id=2 at the same time and i want selected all users from for example group 2 and 4 (cause in this example test1 said so) without records duplicate
All groups from one UserId (say UserId 10):
select GroupId from Assignment where UserId = 10
Select all users from those groups (without duplicate):
select distinct UserId
from dbo.GetGroupUsers() ggu
where ggu.GroupId in (select GroupId from Assignment where UserId = 10)
I hope this is what you wanted.
An inner self join should get you the IDs of the users you're looking for. Join the result with your user table (which you didn't post) to possibly get other information about these users.
SELECT DISTINCT
a2.userid
FROM assignment a1
INNER JOIN assignment a2
ON a2.groupid = a1.groupid
WHERE a1.userid = ?;
(Replace the ? with the ID of the user, you want to start with.)
Assuming your input is a user id:test1 and assuming that you are just looking at one table (Assignment)
DECLARE #UserId INT = 2
;WITH GROUPS AS
(
SELECT DISTINCT GroupId FROM Assignment WHERE UserId = #UserId
)
SELECT distinct assgn.UserName, gps.GroupId FROM Assignment assgn
INNER JOIN
GROUPS gps ON
assgn.GroupId = gps.GroupId
Please let me know if this helps
i have two tables. First table (Class) is table in which there are classes which student can choose, and last column(numberOfRegistration) is the number of registration per class. Here is the first table:
idClass int;
name varchar(50);
date varchar(50);
state bit;
description nvarchar(50);
numberOfRegistration int
Second table (Registration) is for registration:
idRegistration int;
dateOfRegistration date;
name varchar(50);
lastName varchar(50);
city nvarchar(50);
adress nvarchar(50);
postalNumber int;
idClass int - this is foreign key, references idClass from Class table ;
Does anyone have idea how to get the number of registration per class in table Registration , and to write that data into last column(numberOfRegistration) in table Class.
Thank you
Try this:
UPDATE c
SET numberOfRegistration = COUNT(0)
FROM Class c
LEFT JOIN Registration r ON r.idClass = c.idClass
GROUP BY r.IdClass;
You can use a derived table to combine the group by results with the class table, like this:
SELECT name, NumberOfRegistrations
FROM Class
LEFT JOIN
(
SELECT idClass, count(*), NumberOfRegistrations
FROM Registration
GROUP BY idClass
) D ON class.idClass = D .idClass
You can think building two steps for a complete solution.
First step is updating the Class table with current values as a starting point.
As second step, you can create a SQL trigger to keep the registration counts up to date for the related class
Here is an Update script for first step
update class
set numberOfRegistration = cnt
from class c
left join (
select r.idClass, COUNT(r.idRegistration) cnt
from class c
left join Registration r on c.idClass = r.idClass
group by r.idClass
) t on c.idClass = t.idClass
And this is the SQL trigger code. I created the sample SQL trigger as After Insert, Update, Delete trigger as seen in below code
Create TRIGGER dbo.Registration_changes
ON dbo.Registration
AFTER INSERT, UPDATE, DELETE
AS
WITH CTE AS (
select idClass from inserted
union all
select idClass from deleted
), cte2 as (
select CTE.idClass, COUNT(r.idRegistration) cnt
from cte
left join Registration r on CTE.idClass = r.idClass
Group By CTE.idClass
)
update Class
set numberOfRegistration = cte2.cnt
from cte2
where class.idClass = cte2.idClass
GO
I hope it helps,
You can use the below query :
select idClass, count(*) as [NumberOfRegitsration PerClass]
from Registration
group by idClass
I am trying to figure out some ways to accomplish this script. I import an excel sheet and then I need to populate 5 different tables based on this excel sheet. However for this example I just need help with the initial loop then I think I can work through the rest.
select distinct Department from IPACS_New_MasterList
where Department is not null
This provides me a list of 7 different departments.
Dep1, Dep2, Dep3, Dep4, Dep5, Dep6, Dep7
For each of these departments I need to perform some code.
Step #1:
Insert the department into table_one
I then need to keep the SCOPE_IDENTITY() for the rest of the code.
Step #2
perform the second loop (inserting all functions in that department into table2.
I'm not sure how to really do a foreach row in this select statement loop, or if I need to do something completely different. I've looked at several answers but can't seem to find exactly what I'm looking for.
Sample Data:
Source Table
Dep1, func1, process1, procedure1
dep1, func1, process1, procedure2
dep1, func1, process2, procedure3
dep1, func1, process2, procedure4
dep1, func1, process2, procedure5
dep1, func2, process3, procedure6
dep2, func3, process4, procedure7
My Tables:
My first table is a list of every department from the above query. With a key on the departmentID. Each department can have many functions.
My second table is a list of all functions with a key on functionID and a foreign key on departmentID. Each function must have 1 department and can have many processes
My third table is a list of all processes with a key on processID and a foreign key on functionID. Each process must have 1 function and can have many procedures.
There are two approaches you can use without a loop.
1) If you have candidate keys in your source (department name) just join your source table back to the table you inserted
e.g.
INSERT INTO Department
(Name)
SELECT DISTINCT Dep1
FROM SOURCE;
INSERT INTO Functions
(
Name,
DepartmentID)
SELECT DISTINCT
s.Func1,
d.DepartmentID
FROM
source s
INNER JOIN Department d
on s.dep1 = d.name;
INSERT INTO
processes
(
name,
FunctionID,
[Procedure]
)
SELECT
s.process1,
f.FunctionID,
s.procedure1
FROM
source s
INNER JOIN Department d
on s.dep1 = d.name
INNER JOIN Functions f
on d.DepartmentID = f.departmentID
and s.func1 = f.name;
SQL Fiddle
2) If you don't have candidate keys in your source then you can use the output clause
For example here if a department weren't guaranteed to be unique this would correctly find only the newly add
DECLARE #Department TABLE
(
DepartmentID INT
)
DECLARE #Functions TABLE
(
FunctionID INT
)
INSERT INTO Department
(Name)
OUTPUT INSERTED.DepartmentID INTO #Department
SELECT DISTINCT Dep1
FROM SOURCE
INSERT INTO Functions
(
Name,
DepartmentID)
OUTPUT INSERTED.FunctionID INTO #FunctionID
SELECT DISTINCT
s.Func1,
d.DepartmentID
FROM
source s
INNER JOIN Department d
on s.dep1 = d.name
INNER JOIN #Department d2
ON d.departmentID = d2.departmentID;
INSERT INTO
processes
(
name,
FunctionID,
[Procedure]
)
SELECT
s.process1,
f.FunctionID,
s.procedure1
FROM
source s
INNER JOIN Department d
on s.dep1 = d.name
INNER JOIN Functions f
on d.DepartmentID = f.departmentID
and s.func1 = f.name
INNER JOIN #Functions f2
ON f.Functions = f2.Functions
SELECT * FROM Department;
SELECT * FROm Functions;
SELECT * FROM processes;
SQL Fiddle
If I am understanding what you are trying to do... yes you can use a loop. Its not really talked about and I bet I am going to get some feedback from other SQL developers that its not a best practice. But if you really need to do a loop
DECLARE #rowcount as int
DECLARE #numberOfRows as int
SET #rowcount = 0
SET #numberOfRows = SELECT COUNT(*) from tablename --put in anything to get the number of times to loop.
WHILE #numberOfRows <= #rowcount
BEGIN
--Put whatever process you need to repeat here
SET #rowcount = #rowcount + 1
END
Assuming you have tables set up with an IDENTITY field set for the Primary Key, you can populate each successive table's foreign key by joining to the previous table and the source table, something like:
INSERT INTO Table1
SELECT DISTINCT Department
FROM SourceTable
GO
INSERT INTO Table2
SELECT DISTINCT b.Deptartment_ID, a.Function
FROM SourceTable a
JOIN Table1 b
ON a.Department = b.Department
GO
INSERT INTO Table3
SELECT DISTINCT b.Function_ID, a.Process
FROM SourceTable a
JOIN Table2 b
ON a.Function = b.Function
GO
INSERT INTO Table4
SELECT DISTINCT b.Process_ID, a.Procedure
FROM SourceTable a
JOIN Table3 b
ON a.Process = b.Process
GO
I've 2 tables:
nameTable
userName
userId
classId
marksTable
userId
classId
courseCode
marks
i want to display userId, userName, courseCode, marks of all the students having same classId.
create proc mark__classId
#classId int
as
select marksTable.courseCode, marksTable.userId, marksTable.marks, nameTable.userName
from marksTable, nameTable
where
marksTable.classId = nameTable.classId
but this query gave very vague o/p.
Suppose "name1" with id NAME1 with classId 10 follows courseCode 'C1, C2, C3' with respective marks '80,99,90'
now I want to display all this information when I give input as
exec mark__classId 10
Your select should be more like this, you just need to use your incoming parameters. As well as you should be using a JOIN instead of a CROSS JOIN with a filter
SELECT marksTable.courseCode, marksTable.userId, marksTable.marks, nameTable.userName
FROM marksTable
JOIN nameTable
ON marksTable.classId = nameTable.classId
WHERE marksTable.classId = #classId
I have wrote the following query which extracts all users which are in child departments of current user's department.
Current user cames from client app, but I Decalred it here in SQL for tests reason.
DECLARE #UserID INT = 72
SELECT *
FROM users
WHERE Department_Id IN (
SELECT DISTINCT Id /*, idp*/
FROM departments
WHERE idp IN (
SELECT Department_Id
FROM users
WHERE Id = #UserID
)
)
OR Department_Id IN (
SELECT DISTINCT idp
FROM departments
WHERE idp IN (
SELECT Department_Id
FROM users
WHERE Id = #UserID
)
)
I wanted to select the Id and the idp from departments to have a short query, but when i use this way it returns me an SQL error :
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
This is because my list should contain only one column, but not 2.
Please advice me any way to reduce this query, especially the second part (after OR) which is a copy-paste of first (before OR)
Thank you.
Try using EXISTS like this
SELECT *
FROM users u
WHERE EXISTS( SELECT *
FROM departments
WHERE idp IN (SELECT Department_Id FROM users WHERE Id = #UserID)
AND (id = u.Department_Id
OR idp = u.Department_Id) )
A few thoughts...
Nested IN subqueries are unlikely to be friendly.
You don't need DISTINCT when using IN
I'd use GROUP BY to deal with the 1:many relationships, but as your answer is using an alternative structure, I'll try to keep close to what you have...
DECLARE #UserID INT = 72
SELECT
*
FROM
users AS associates
WHERE
EXISTS (
SELECT
*
FROM
users
INNER JOIN
departments
ON departments.idp = users.Department_Id
WHERE
users.id = #user_id
AND ( departments.id = associates.department_id
OR departments.idp = associates.department_id)
)
If you did use the GROUP BY approach, you avoid sub-queries and correlated-sub-queries all together...
DECLARE #UserID INT = 72
SELECT
associates.id
FROM
users
INNER JOIN
departments
ON departments.idp = users.Department_Id
INNER JOIN
users AS associates
ON associates.department_id = departments.id
OR associates.department_id = departments.idp
WHERE
users.id = #user_id
GROUP BY
associates.id
If there any other fields in associates that you need, just add them to the SELECT and the GROUP BY.
SELECT *
FROM users
WHERE Department_Id IN (
SELECT myId FROM
( SELECT Id AS myId
FROM departments
UNION ALL
SELECT idp AS myId
FROM departments
) A
WHERE A.myId IN (
SELECT Department_Id
FROM users
WHERE Id = #UserID
)
)