How to count the number of ROWS in SQL table - sql

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

Related

Delete rows from multiple tables with foreign keys

I have three tables
userTable with following rows Id, FirstName, LastName, Email
Product A with following rows Id, UserId(FK), startDate
Product D with following rows Id, UserId(FK), startDate
I want to delete rows from three tables with particular UserId.
What I want to achieve -
Find the id given email (select id from userTable where Email = 'abc.com') - got back id - 3
Delete from Product A where id = 3
Delete from Product D where id = 3
Delete from userTable where id = 3
I want to create a script.
Any help is appreciated or learning reference would be great.
Declare a variable and save the id to that variable. Use that Id for the delete query.
DECLARE #Id INT;
SELECT #Id=id
FROM userTable
WHERE Email = 'abc.com'
DELETE FROM Product A WHERE id = #Id
DELETE FROM Product D WHERE id = #Id
DELETE FROM userTable WHERE id = #Id
But I guess in your case you have UserId as the FK so you should be trying this:
DELETE FROM Product A WHERE UserId= #Id
DELETE FROM Product D WHERE UserId= #Id
DELETE FROM userTable WHERE id = #Id
Note: If you want to do the same thing what's there in question, go for 1st one. If you want to delete the relevant user records from A and D table then go for the 2nd method where you are deleting the records using the FK.
From what I can gather from your question you want something like this:
DELETE FROM
userTable,
ProductA,
ProductB
WHERE
userTable.Id = (select Id from userTable where Email like 'abc.com') AND
userTable.Id = ProductA.Id AND
userTable.Id = ProductB.Id;
And the Email in the subquery in the WHERE clause would be your input parameter
DISCLAIMER: From a Sybase/SQL Anywhere background, this would be possible, I'm not sure if it could be done in SQL-server.

SQL Loop/Crawler

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

Pre-Populate foreign keys in SQL with a new row

I am trying to write a SQL script which will go through a table with a newly created FK and pre-populate the key with a new row in the foreign table. I'm not 100% on how to do this or if its even possible in a single statement but here's my attempt:
UPDATE [dbo].[Blogs]
set AuthorSecurableId = inserted.Id
FROM [dbo].[Blogs] updating
INNER JOIN
(INSERT INTO [dbo].[Securables] (Name)
OUTPUT Inserted.id, ROW_NUMBER() OVER (ORDER BY Inserted.Id) as rownum
SELECT 'Admin : ' + Name
FROM Blogs
WHERE AuthorSecurableId is null) inserted ON ROW_NUMBER() OVER (ORDER BY updating.Id) = inserted.rownum
WHERE updating.AuthorSecurableId is null
When I do this I get the following error
Msg 10727, Level 15, State 1, Line 5
A nested INSERT, UPDATE, DELETE, or MERGE statement is not allowed on either side of a JOIN or APPLY operator.
Below is a simple view of the schema I have
I would like to create a securable for each blog which doesn't have one and populate that blogs AuthorSecurableId with the ID of the newly created securable
I think I could do this with a cursor but I was wondering if there is a better single statement approach
You can use merge against Securables with output to a table variable or temp table and a separate update against Blogs.
declare #T table
(
SecID int,
BlogID int
);
merge Securables T
using (
select 'Admin : '+B.Name as Name, B.Id
from Blogs as B
where B.AuthorSecurabledId is null
) as S
on 0 = 1
when not matched by target then
insert (Name) values (S.Name)
output inserted.Id, S.Id
into #T;
update Blogs
set AuthorSecurabledId = T.SecID
from #T as T
where Blogs.Id = t.BlogID;
SQL Fiddle

Joining Data Between 2 SQL Tables

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.

Subtract counts returned from two tables using tsql

I have two tables called A and B.
Table A contains request details such as
request_id, company_id, and Customer_id.
Table B contains feedback data such as feedback_id, company_id, status_ind,customer_id, score_total.
I need to get the count of rows of table B which has corresponding company_id with table A where the status_ind is 1 and subtract from the count of table A.
I'm not sure what you're asking but
declare #countA integer = (select count(*) from A)
declare #countB integer =
(select count(*) from B where
exists(select * from A where A.company_id = B.company_id AND A.status_ind = 1)
declare #difference integer = #countA - #countB
you have to use a sql stored procedure or function.
Seems like your problem can be rephrased like this: count rows of A that have no matches with the status_ind = 1 rows of B.
Well, just use an anti-join, like this, for instance:
SELECT COUNT(*)
FROM A
LEFT JOIN B ON A.company_id = B.company_id AND B.status_ind = 1
WHERE B.company_id IS NULL
I am also not sure. But i understood it like this:
SELECT COUNT(*) FROM A WHERE company_id NOT IN (SELECT company_id FROM B WHERE status_ind = 1)
I edited it, i thought first that status_ind is in table A not in B