how to write SQL query in this situation - sql

I have table named "Attendance" which looks like this:
Sno SecurityGroup SecurityName Designation AttendanceStatus
----------------------------------------------------------------
1 JJ Ram officer present
2 JJ Raja Guards Present
3 JJ Rani LadyGuards Present
4 JJ Ramu officer present
I need the Output as count of number of securities present in each Designation as follows:
SecutityGroup Officer Guards LadyGuards
-----------------------------------------
JJ 2 1 1
Can someone please help me write a query to get this Output?

select SecurityGroup,
sum(case when Designation = 'officer' then 1 end) as Officer,
sum(case when Designation = 'Guards' then 1 end) as Guards,
sum(case when Designation = 'LadyGuards' then 1 end) as LadyGuards
from Attendance
group by SecurityGroup
Alternately, if you are OK with having the information in rows instead, you can do:
select SecurityGroup, Designation, count(*) as Count
from Attendance
group by SecurityGroup, Designation
Obviously the second approach is preferred as it is less brittle, and will function if more Designations get added without any modification.

This can also be done with a PIVOT, depending on your database:
SELECT SecurityGroup, SUM([officer]) AS Officers, SUM([Guards]) AS Guards, SUM([LadyGuards]) AS LadyGuards
FROM Attendance
PIVOT
(
COUNT(Sno)
FOR Designation IN ([officer], [Guards], [LadyGuards])
) as pvt
WHERE AttendanceStatus = 'Present'
GROUP BY SecurityGroup
If you want to have the column list generated dynamically based on whatever is in the table, it gets harder, but this avoids the needs for lots of subqueries.

I Tried out using PIVOT in SQL, But i could not Get count value..
Following is Code i tried:
select SecurityGroup,Officer,Guards,LadyGuards from
(select SecurityGroup,rDesignation from Attendance
where SecurityGroup='jj') up
PIVOT (count(Designation) for Designation IN
(Officer,Guards,LadyGuards)) as pvt
When i Execute this Query, I get
SecurityGroup,Officer,Guards,LadyGuards
JJ,0,0,0
Instead of,
SecurityGroup,Officer,Guards,LadyGuards
JJ,2,1,1

select distinct SecurityGroup,
select sum(*) from Attendance where designation = 'officer') as Officer,
select sum(*) from Attendance where designation = 'Guards') as Guards,
select sum(*) from Attendance where designation = 'LadyGuards') as LadyGuards
from Attendance

You can use sub queries to get this accomplished:
SELECT
SecurityGroup,
(SELECT COUNT(*) FROM `Attendance` WHERE `Designation` = "officer") AS `Officer`,
(SELECT COUNT(*) FROM `Attendance` WHERE `Designation` = "Guards") AS `Guards`,
(SELECT COUNT(*) FROM `Attendance` WHERE `Designation` = "LadyGuards") AS `LadyGuards`
FROM `Attendance`
WHERE `SecurityGroup` = "JJ"
I haven't actually tested this query, as I just wanted to share the concept with you.
Also please do note that is not the fastest way of accomplishing what you need done, but I believe it's the simplest way possible.
I hope this works for you.

Related

SQL: multiple counts from same table

I am having a real problem trying to get a query with the data I need. I have tried a few methods without success. I can get the data with 4 separate queries, just can't get hem into 1 query. All data comes from 1 table. I will list as much info as I can.
My data looks like this. I have a customerID and 3 columns that record who has worked on the record for that customer as well as the assigned acct manager
RecID_Customer___CreatedBy____LastUser____AcctMan
1-------1374----------Bob Jones--------Mary Willis------Bob Jones
2-------1375----------Mary Willis------Bob Jones--------Bob Jones
3-------1376----------Jay Scott--------Mary Willis-------Mary Willis
4-------1377----------Jay Scott--------Mary Willis------Jay Scott
5-------1378----------Bob Jones--------Jay Scott--------Jay Scott
I want the query to return the following data. See below for a description of how each is obtained.
Employee___Created__Modified__Mod Own__Created Own
Bob Jones--------2-----------1---------------1----------------1
Mary Willis------1-----------2---------------1----------------0
Jay Scott--------2-----------1---------------1----------------1
Created = Counts the number of records created by each Employee
Modified = Number of records where the Employee is listed as Last User
(except where they created the record)
Mod Own = Number of records for each where the LastUser = Acctman
(account manager)
Created Own = Number of Records created by the employee where they are
the account manager for that customer
I can get each of these from a query, just need to somehow combine them:
Select CreatedBy, COUNT(CreatedBy) as Created
FROM [dbo].[Cust_REc] GROUP By CreatedBy
Select LastUser, COUNT(LastUser) as Modified
FROM [dbo].[Cust_REc] Where LastUser != CreatedBy GROUP By LastUser
Select AcctMan, COUNT(AcctMan) as CreatePort
FROM [dbo].[Cust_REc] Where AcctMan = CreatedBy GROUP By AcctMan
Select AcctMan, COUNT(AcctMan) as ModPort
FROM [dbo].[Cust_REc] Where AcctMan = LastUser AND NOT AcctMan = CreatedBy GROUP By AcctMan
Can someone see a way to do this? I may have to join the table to itself, but my attempts have not given me the correct data.
The following will give you the results you're looking for.
select
e.employee,
create_count=(select count(*) from customers c where c.createdby=e.employee),
mod_count=(select count(*) from customers c where c.lastmodifiedby=e.employee),
create_own_count=(select count(*) from customers c where c.createdby=e.employee and c.acctman=e.employee),
mod_own_count=(select count(*) from customers c where c.lastmodifiedby=e.employee and c.acctman=e.employee)
from (
select employee=createdby from customers
union
select employee=lastmodifiedby from customers
union
select employee=acctman from customers
) e
Note: there are other approaches that are more efficient than this but potentially far more complex as well. Specifically, I would bet there is a master Employee table somewhere that would prevent you from having to do the inline view just to get the list of names.
this seems pretty straight forward. Try this:
select a.employee,b.created,c.modified ....
from (select distinct created_by from data) as a
inner join
(select created_by,count(*) as created from data group by created_by) as b
on a.employee = b.created_by)
inner join ....
This highly inefficient query may be a rough start to what you are looking for. Once you validate the data then there are things you can do to tidy it up and make it more efficient.
Also, I don't think you need the DISTINCT on the UNION part because the UNION will return DISTINCT values unless UNION ALL is specified.
SELECT
Employees.EmployeeID,
Created =(SELECT COUNT(*) FROM Cust_REc WHERE Cust_REc.CreatedBy=Employees.EmployeeID),
Mopdified =(SELECT COUNT(*) FROM Cust_REc WHERE Cust_REc.LastUser=Employees.EmployeeID AND Cust_REc.CreateBy<>Employees.EmployeeID),
ModOwn =
CASE WHEN NOT Empoyees.IsManager THEN NULL ELSE
(SELECT COUNT(*) FROM Cust_REc WHERE AcctMan=Employees.EmployeeID)
END,
CreatedOwn=(SELECT COUNT(*) FROM Cust_REc WHERE AcctMan=Employees.EmployeeID AND CReatedBy=Employees.EMployeeID)
FROM
(
SELECT
EmployeeID,
IsManager=CASE WHEN EXISTS(SELECT AcctMan FROM CustRec WHERE AcctMan=EmployeeID)
FROM
(
SELECT DISTINCT
EmployeeID
FROM
(
SELECT EmployeeID=CreatedBy FROM Cust_Rec
UNION
SELECT EmployeeID=LastUser FROM Cust_Rec
UNION
SELECT EmployeeID=AcctMan FROM Cust_Rec
)AS Z
)AS Y
)
AS Employees
I had the same issue with the Modified column. All the other columns worked okay. DCR example would work well with the join on an employees table if you have it.
SELECT CreatedBy AS [Employee],
COUNT(CreatedBy) AS [Created],
--Couldn't get modified to pull the right results
SUM(CASE WHEN LastUser = AcctMan THEN 1 ELSE 0 END) [Mod Own],
SUM(CASE WHEN CreatedBy = AcctMan THEN 1 ELSE 0 END) [Created Own]
FROM Cust_Rec
GROUP BY CreatedBy

Select all unique names based on most recent value in different field

I have an access database with a table called SicknessLog. The fields are ID, StaffName, [Start/Return], DateStamp.
When a member of staff is off work for sickness then a record is added to the table and the value in the [Start/Return] field is 1. When they return to work a new record is added with the same details except the [Start/Return] field is 0.
I am trying to write a query that will return all distinct staff names where the most recent record for that person has a value of 1 (ie, all staff who are still off sick)
Does anyone know if this is possible? Thanks in advance
Here's one way, all staff that has been sick where it does not exist an event after that where that staff is "nonsick":
select distinct x.staffname
from sicknesslog x
where Start/Return = 1
and not exists (
select 1
from sicknesslog y
where x.StaffName = y.StaffName
and y.DateStamp > x.DateStamp
and y.Start/Return = 0
)
You can use group by to achieve this.
select staffname ,max(datestamp) from sicknesslog where start/return = 1 group by staffname
it will return all latest recored for all staff. If ID column is autogenerated PK then you can use it in max function.
select staffname,MAX(datestamp)
from sicknesslog
where [start/return]=1
group by staffname
order by max(datestamp) desc,staffname
This will retrieve latest records who is sick and off to work
This should be close:
select s.StaffName, s.DateStamp, s.[Start/Return]
from SicknessLog s
left join (
select StaffName, max(DateStamp) as MaxDate
from SicknessLog
group by StaffName
) sm on s.StaffName = sm.StaffName and s.DateStamp = sm.MaxDate and s.[Start/Return] = 1

Stored procedure for getting sum of entries in table for each ID

I am writing a stored procedure, where I have a column called scale which stores the result from the radio button selected as 1/ 2/ 3/ 4 for each type of skill name.
Now, I want to see total number of people under each scale- 1 and 2 and 3 and 4 for a particular skillname1, skillname 2,..., skillname20.
Here is my table:
tblSkill:
ID | SkillName
and another table as:
tblskillMetrics:
ID | SkillID | EmployeeID | Scale
And here is the query am trying to write:
Create Procedure spGetSkillMetricsCount
As
Begin
SELECT
tblSkill.Name as skillname,
(select COUNT(EmployeeID) from tblSkillMetrics where tblSkillMetrics.Scale=1) AS NotAplicable,
(select COUNT(EmployeeID) from tblSkillMetrics where tblSkillMetrics.Scale=2 ) AS Beginner,
(select COUNT(EmployeeID) from tblSkillMetrics where tblSkillMetrics.Scale=3 ) AS Proficient,
(select COUNT(EmployeeID) from tblSkillMetrics where tblSkillMetrics.Scale=4 ) AS Expert
FROM
tblSkill
INNER JOIN
tblSkillMetrics ON tblSkillMetrics.SkillID = tblSkill.ID
GROUP BY
tblSkillMetrics.Scale, tblSkill.Name
ORDER BY
skillname DESC
END
By using this stored procedure, I am able to get the desired format in which I want the result but in the output for each : Not Applicable, Beginner, Proficient or Expert is same and it is sum of all the entries made in the table.
Please can someone suggest where am I going wrong.
Logically, you are grouping by two criteria, scale and skill name. However, if I understand it correctly, every row is supposed to represent a single skill name. Therefore, you should group by tblSkill.Name only. To get different counts for different scales in separate columns, you can use conditional aggregation, i.e. aggregation on an expression that (usually) involves a CASE construct. Here's how you could go about it:
SELECT
tblSkill.Name AS skillname,
COUNT(CASE tblSkillMetrics.Scale WHEN 1 THEN EmployeeID END) AS NotAplicable,
COUNT(CASE tblSkillMetrics.Scale WHEN 2 THEN EmployeeID END) AS Beginner,
COUNT(CASE tblSkillMetrics.Scale WHEN 3 THEN EmployeeID END) AS Proficient,
COUNT(CASE tblSkillMetrics.Scale WHEN 4 THEN EmployeeID END) AS Expert
FROM
tblSkill
INNER JOIN
tblSkillMetrics ON tblSkillMetrics.SkillID = tblSkill.ID
GROUP BY
tblSkill.Name
ORDER BY
skillname DESC
;
Note that there's a special syntax for this kind of queries. It employs the PIVOT keyword, as what you get is essentially a grouped result set pivoted on one of the grouping criteria, scale in this case. This is how the same could be achieved with PIVOT:
SELECT
skillname,
[1] AS NotAplicable,
[2] AS Beginner,
[3] AS Proficient,
[4] AS Expert
FROM (
SELECT
tblSkill.Name AS skillname,
tblSkillMetrics.Scale,
EmployeeID
FROM
tblSkill
INNER JOIN
tblSkillMetrics ON tblSkillMetrics.SkillID = tblSkill.ID
) s
PIVOT (
COUNT(EmployeeID) FOR Scale IN ([1], [2], [3], [4])
) p
;
Basically, PIVOT implies grouping. All columns but one in the source dataset are grouping criteria, namely every one of them that is not used as an argument of an aggregate function in the PIVOT clause is a grouping criterion. One of them is also assigned to be the one the results are pivoted on. (Again, in this case it is scale.)
Because grouping is implicit, a derived table is used to avoid grouping by more criteria than necessary. Values of Scale become names of new columns that the PIVOT clause produces. (That is why they are delimited with square brackets when listed in PIVOT: they are not IDs in that context but identifiers delimited as required by Transact-SQL syntax.)
A case construct instead of all those subqueries might work.
select tblSkill.name skillname
, case when tblSkillMetrics = 1 then 'Not Applicable'
etc
else 'Expert' end level
, count(employeeid) records
from tblSkill join tblSkillMetrics
on tblSkillMetrics.SkillID = tblSkill.ID
group by tblSkill.name
, case when tblSkillMetrics = 1 then 'Not Applicable'
etc
else 'Expert' end level
order by skillname desc
You can use something like this:
SELECT sum(case when tblskillmetrics.scale = 2 then 1 else 0 end) Beginner,
sum(case when tblskillmetrics.Scale=3 then 1 else 0 end)Proficient,
SUM(case when tblSkillMetrics.Scale=4 then 1 else 0 end)Expert,
tblSkillGroup.ID AS GroupID,tblSkillGroup.Name AS GroupName,
tblSkill.ID AS SkillID
, tblSkill.Name AS SkillName
FROM tblSkill INNER JOIN tblSkillMetrics
ON tblSkillMetrics.SkillID=tblSkill.ID
ORDER BY GroupName DESC

SQL add multiple "Count" together

I'm trying to add the counts together and output the one with the max counts.
The question is: Display the person with the most medals (gold as place = 1, silver as place = 2, bronze as place = 3)
Add all the medals together and display the person with the most medals
Below is the code I have thought about (obviously doesn't work)
Any ideas?
Select cm.Givenname, cm.Familyname, count(*)
FROM Competitors cm JOIN Results re ON cm.competitornum = re.competitornum
WHERE re.place between '1' and '3'
group by cm.Givenname, cm.Familyname
having max (count(re.place = 1) + count(re.place = 2) + count(re.place = 3))
Sorry forgot to add that were not allowed to use ORDER BY.
Some data in the table
Competitors Table
Competitornum GivenName Familyname gender Dateofbirth Countrycode
219153 Imri Daniel Male 1988-02-02 Aus
Results Table
Eventid Competitornum Place Lane Elapsedtime
SWM111 219153 1 2 20 02
From what you've described it sounds like you just need to take the "Top" individual in the total medal count. In order to do that you would write something like this.
Select top 1 cm.Givenname, cm.Familyname, count(*)
FROM Competitors cm JOIN Results re ON cm.competitornum = re.competitornum
WHERE re.place between '1' and '3'
group by cm.Givenname, cm.Familyname
order by count(*) desc
Without using order by you have a couple of other options though I'm glossing over whatever syntax peculiarities sqlfire may use.
You could determine the max medal count of any user and then only select competitors that have that count. You could do this by saving it out to a variable or using a subquery.
Select cm.Givenname, cm.Familyname, count(*)
FROM Competitors cm JOIN Results re ON cm.competitornum = re.competitornum
WHERE re.place between '1' and '3'
group by cm.Givenname, cm.Familyname
having count(*) = (
Select max( count(*) )
FROM Competitors cm JOIN Results re ON cm.competitornum = re.competitornum
WHERE re.place between '1' and '3'
group by cm.Givenname, cm.Familyname
)
Just a note here. This second method is highly inefficient because we recalculate the max medal count for every row in the parent table. If sqlfire supports it you would be much better served by calculating this ahead of time, storing it in a variable and using that in the HAVING clause.
You are grouping by re.place, is that what you want? You want the results per ... ? :)
[edit] Good, now that's fixed you're almost there :)
The having is not needed in this case, you simply need to add a count(re.EventID) to your select and make a subquery out of it with a max(that_count_column).

SQL Select Query With Count And Where Statement

I always seem to trip myself up during these types of SQL Statements. Here is what I'm attempting to accomplish.
My Example Table
Name Date Type
Bob 9/28/11 1
Bob 9/27/11 1
Bob 9/28/11 2
Debra 9/28/11 1
I'm trying to write a SQL Statement that would give me all the names, their total count occured, and then a Date Filter. I'll write a rought statement with completely wrong syntax, but I think it'll convey what I'm attempting to do...I think.
SELECT Name, Count(*) As Total
FROM Table
GROUP BY Name, Total
WHERE Date = Today (I'm aware you can't do a WHERE in a GROUP BY) AND Type = 1
Essentially, I would like to get back a set of data that would show the Name, how many instances of Type 1 for today.
I don't think I'm searching for the proper question to actually be able to effectively research this on my own as well, probably because I'm wording it improperly.
Any help would be appreciated!
Your WHERE was in the wrong place:
SELECT Name, Count(*) As Total
FROM Table
WHERE Date = Today AND Type = 1
GROUP BY Name
You just need to move your WHERE after your FROM.
SELECT Name, Count(*) As Total
FROM Table
WHERE [Date] = CAST(getdate() as Date) -- or 'Jan 1 1990'
AND [Type] = 1
GROUP BY Name
WHERE comes before GROUP
SELECT Name, Count(*) As Total
FROM Table
WHERE Date >= #Today --assuming your date passed in has no time
AND Date < DATEADD(d,1,#Today)
AND Type = 1
GROUP BY Name, Total
example you can run
SELECT LEFT(name,1) FROM sysobjects
WHERE name < 'p'
GROUP BY LEFT(name,1)