I have just started learning SQL. I've written the following:
DECLARE #DateFrom Date = '01-Jan-2014', #DateTo Date = '31-Dec-2014'
SELECT TotalCalls, UniqueCalls, TotalEmails, UniqueEmails, AgentsContacted, Instructed FROM
( SELECT *,
(SELECT YEAR(EventDate)) AS year,
(SELECT COUNT(*) FROM Events JOIN dbo.ContactType
ON EventContactType=ContactTypeID
WHERE ContactTypeName = 'Call' AND EventDate >= #DateFrom AND EventDate <= #DateTo) AS TotalCalls,
(SELECT COUNT(Distinct EventAgentID) FROM Events JOIN dbo.ContactType
ON EventContactType=ContactTypeID
WHERE ContactTypeName = 'Call' AND EventDate >= #DateFrom AND EventDate <= #DateTo) AS UniqueCalls,
(SELECT COUNT(*) FROM Events JOIN dbo.ContactType
ON EventContactType=ContactTypeID
WHERE ContactTypeName = 'Email' AND EventDate >= #DateFrom AND EventDate <= #DateTo) AS TotalEmails,
(SELECT COUNT(Distinct EventAgentID) FROM Events JOIN dbo.ContactType
ON EventContactType=ContactTypeID
WHERE ContactTypeName = 'Email' AND EventDate >= #DateFrom AND EventDate <= #DateTo) AS UniqueEmails,
(SELECT COUNT(DISTINCT EventAgentID)
FROM Events
JOIN dbo.ContactType
ON EventContactType=ContactTypeID
LEFT JOIN (SELECT AgentID, (SELECT CASE WHEN AgentDateOfRecentInstruction Is Null OR
AgentDateOfRecentInstruction < DATEADD(month, -12, #DateFrom) --for sp change get date to #FromDate
THEN 'NO' ELSE 'YES' END) AS InstructedWithinPastYear FROM Agents) ti
ON Events.EventAgentID=ti.AgentID
WHERE EventToFrom='1'
AND (ContactTypeName = 'Email' OR ContactTypeName = 'Call')
AND InstructedWithinPastYear = 'NO'
AND (EventDate >= #DateFrom AND EventDate <= #DateTo)) AS AgentsContacted,
(SELECT COUNT(DISTINCT EventAgentID)
FROM Events
Join dbo.AGents
ON EventAgentID=AgentID
JOIN dbo.ContactType
ON EventContactType=ContactTypeID
LEFT JOIN (SELECT AgentID, (SELECT CASE WHEN AgentDateOfRecentInstruction Is Null OR
AgentDateOfFirstIntsruction < #DateFrom --for sp change get date to #FromDate
THEN 'NO' ELSE 'YES' END) AS InstructedWithinPastYear FROM Agents) ti
ON Events.EventAgentID=ti.AgentID
WHERE EventToFrom='1'
AND (ContactTypeName = 'Email' OR ContactTypeName = 'Call')
AND (AgentDateOfRecentInstruction <= #DateTo AND AgentDateOfRecentInstruction >= #DateFrom)
AND (EventDate >= #DateFrom AND EventDate <= #DateTo)) AS Instructed
FROM Events
JOIN dbo.ContactType
ON EventContactType=ContactTypeID
Join dbo.AGents
ON EventAgentID=AgentID
)
as s
PIVOT (count(EventAgentID) FOR year
IN ([2013],[2014]) ) pvt
Which returns thousands of row of this:
TotalCalls | UniqueCalls | TotalEmails | UniqueEmails | AgentsContacted | Instructed
169 | 106 | 202 | 125 | 24 | 15
169 | 106 | 202 | 125 | 24 | 15
169 | 106 | 202 | 125 | 24 | 15
I want to it to return:
Year | TotalCalls | UniqueCalls | TotalEmails | UniqueEmails | AgentsContacted
2014 | 169 | 106 | 202 | 125 | 24
2013 | 69 | 68 | 112 | 86 | 13
I'm fairly certain I'm doing a few things wrong here but I wasn't able to write my question succinctly enough to work out how to do it from searches.
(note: The local variables are there temporarily whilst I write the procedure)
You could probably simplify this query greatly by putting everything into one query statement, rather than a series of individual queries in the SELECT statement. Right now, you have "SELECT (SELECT Blah1FROM X), (SELECT Blah2 FROM X)" where you could just as easily write "SELECT Blah1, Blah2 FROM X" with a little creativity.
You have a couple of basic flaws in your code that are causing your problem - you're aggregating every row in each of your SELECT queries, so the data you're returning is actually the count across all records, not for the year you want. In addition, you never group your results (look up the GROUP BY statement) so you're returning thousands of rows, one for each detail record, rather than the single row per year I think you want.
Consider writing something more like the following, which uses aggregations against a single query with the GROUP BY.
DECLARE #DateFrom Date = '01-Jan-2014', #DateTo Date = '31-Dec-2014'
SELECT
YEAR(EventDate) AS [Year],
COUNT(CASE WHEN ContactTypeName = 'Call' THEN EventAgentID END) AS TotalCalls, -- You might need the "EventDate >= #DateFrom AND EventDate <= #DateTo" as part of this CASE statement
COUNT(DISTINCT CASE WHEN ContactTypeName = 'Call' THEN EventAgentID END) AS UniqueCalls,
COUNT(CASE WHEN ContactTypeName = 'Email' THEN EventAgentID END) AS TotalEmails,
COUNT(DISTINCT CASE WHEN ContactTypeName = 'Email' THEN EventAgentID END) AS UniqueEmails,
COUNT(DISTINCT
CASE
WHEN
EventToFrom='1' AND
ContactTypeName IN ('Call', 'Email') AND
(AgentDateOfRecentInstruction IS NULL OR AgentDateOfRecentInstruction < DATEADD(month, -12, #DateFrom)) AND
(EventDate >= #DateFrom AND EventDate <= #DateTo)
THEN EventAgentID
END) AS AgentsContacted,
COUNT(DISTINCT
CASE
WHEN
EventToFrom='1' AND
ContactTypeName IN ('Call', 'Email') AND
AgentDateOfRecentInstruction <= #DateTo AND AgentDateOfRecentInstruction >= #DateFromAND
(EventDate >= #DateFrom AND EventDate <= #DateTo)
THEN EventAgentID
END) AS AgentsContacted
FROM
dbo.Events
INNER JOIN
dbo.ContactType ON
EventContactType=ContactTypeID
INNER JOIN
dbo.Agents ON
EventAgentID=AgentID
GROUP BY YEAR(EventDate)
Lacking any sample data or even really a schema, I can't guarantee that this conversion will work without any tweaks - you'll likely have to take this code, check for spelling or syntax errors, then go through each statement to make sure it's actually doing what you want it to. It should, however, get you started.
Related
Recently I've been tasked with creating a report that outputs sales information by Date of Business and Hour of the Day.
Here is the query I have currently written.
WITH CTE AS
(
SELECT 0 AS Count
UNION ALL
SELECT Count + 1
FROM CTE
WHERE Count + 1 <= 23
),
ALLDATES AS
(
SELECT CONVERT(datetime, #startDate) AS [DOB]
UNION ALL
SELECT DATEADD(DAY, 1, [DOB])
FROM AllDates
WHERE [DOB] < #endDate
)
SELECT D.DOB, A.Count AS [Hour], CONCAT(A.Count, ':00') AS [DisplayHour]
, B.OrderModeName, COALESCE(B.Sales_Total, 0) AS [Sales]
, COALESCE(B.Comps, 0) AS Comps, COALESCE(B.Promos, 0) AS Promos
FROM CTE AS A
OUTER APPLY (SELECT DOB FROM ALLDATES) D
LEFT OUTER JOIN (
SELECT DATEPART(HH, ItemDetail.TransactionTime) AS [Hour]
, OrderMode.OrderModeName, SUM(ItemDetail.GrossPrice) Sales_Total
, SUM(CompAmount) AS Comps, SUM(PromoAmount) AS Promos
FROM ItemDetail
INNER JOIN OrderMode ON OrderMode.OrderModeID = ItemDetail.OrderModeID
WHERE ItemDetail.DOB = D.DOB /*NEED HELP HERE*/ AND LocationID IN (
SELECT LocationID
FROM LocationGroupMember
WHERE LocationGroupID = '#locationGroupID'
)
GROUP BY ItemDetail.DOB, DATEPART(HH, ItemDetail.TransactionTime), OrderMode.OrderModeName
) AS B
ON A.Count = B.Hour
ORDER BY D.DOB, A.Count
Where I am struggling is being able to reference the current row's DOB column that is coming from the OUTER APPLY.
I have tried WHERE ItemDetail.DOB = D.DOB, however I receive an error that the identifier can't be bound. Am I correct that in understanding that the outer applied data is not visible to the subquery within the join?
Here is an example of the output I'm expecting:
DOB | Hour | Display Hour | OrderModeName | Sales | Comps | Promos
1/8/2020 | 17 | 17:00 | Order | 163.17 | 0 | 0 <-- Sales for Hour and Order Mode present
1/8/2020 | 23 | 23:00 | | 0 | 0 | 0 <-- No sales at all for a given hour
Thanks in advance for any direction and advice!
The basic pattern here is to CROSS JOIN to define the result "grain" and then LEFT JOIN the fact table to populate the rows for which data exists. EG
WITH CTE AS
(
SELECT 0 AS Count
UNION ALL
SELECT Count + 1
FROM CTE
WHERE Count + 1 <= 23
),
ALLDATES AS
(
SELECT CONVERT(datetime, #startDate) AS [DOB]
UNION ALL
SELECT DATEADD(DAY, 1, [DOB])
FROM AllDates
WHERE [DOB] < #endDate
),
ALLHOURS as
(
SELECT D.DOB, A.Count AS [Hour], CONCAT(A.Count, ':00') AS [DisplayHour]
FROM CTE AS A
CROSS JOIN ALLDATES D
),
ITEM_SUMMARY as
(
SELECT DOB, DATEPART(HH, ItemDetail.TransactionTime) AS [Hour], OrderMode.OrderModeName, SUM(ItemDetail.GrossPrice) Sales_Total, SUM(CompAmount) AS Comps, SUM(PromoAmount) AS Promos
FROM ItemDetail
INNER JOIN OrderMode ON OrderMode.OrderModeID = ItemDetail.OrderModeID
AND LocationID IN (SELECT LocationID FROM LocationGroupMember WHERE LocationGroupID = #locationGroupID)
where DOB >= #startDate
and DOB < #endDate
GROUP BY ItemDetail.DOB, DATEPART(HH, ItemDetail.TransactionTime), OrderMode.OrderModeName
)
select ALLHOURS.DOB,
ALLHOURS.Count AS [Hour],
CONCAT(ALLHOURS.Count, ':00') AS [DisplayHour],
ITEM_SUMMARY.OrderModeName,
COALESCE(ITEM_SUMMARY.Sales_Total, 0) AS [Sales],
COALESCE(ITEM_SUMMARY.Comps, 0) AS Comps,
COALESCE(ITEM_SUMMARY.Promos, 0) AS Promos
from ALLHOURS
LEFT OUTER JOIN ITEM_SUMMARY
on ITEM_SUMMARY.DOB = ALLHOURS.DOB
and ITEM_SUMMARY.Hour = ALLHOURS.Hour
I have the following query that I simplified. Simply it displays a list of counts of records in the last 4 weeks.
SELECT COUNT(*) AS [Counts], Week=1
FROM TableA
WHERE Date >= DATEADD(week,-1,GETDATE())
AND Date <= GETDATE()
UNION
SELECT COUNT(*), 2
FROM TableA
WHERE Date >= DATEADD(week,-2,GETDATE())
AND Date <= DATEADD(week,-1,GETDATE())
UNION
SELECT COUNT(*), 3
FROM TableA
WHERE Date >= DATEADD(week,-3,GETDATE())
AND Date <= DATEADD(week,-2,GETDATE())
UNION
SELECT COUNT(*), 4
FROM TableA
WHERE Date >= DATEADD(week,-4,GETDATE())
AND Date <= DATEADD(week,-3,GETDATE())
Returns:
----------------
| Count | Week |
----------------
| 20 | 1 |
----------------
| 10 | 2 |
----------------
| 30 | 3 |
----------------
| 25 | 4 |
----------------
Suppose I want to modify the query so it returns the last 10 or 20 weeks.
How can I shorten the query so it loops through weeks?
e.g.
declare #w int;
set #w = 10;
while #w <> 0
begin
...;
--how can I do union joins?
set #w = #w - 1;
end
You could avoid UNION and specifying each week by hand by using GROUP BY:
SELECT datepart(week, Date) AS WeekNum, COUNT(*) AS counts
FROM TableA
WHERE Date >= DATEADD(week,-20,GETDATE()) -- num of weeks
GROUP BY datepart(week, Date); -- week of the year
If you need nums from 1 to n then:
WITH cte AS (
SELECT datepart(year, Date) AS [year],
datepart(week, Date) AS WeekNum,
COUNT(*) AS counts
FROM TableA
WHERE Date >= DATEADD(week,-20,GETDATE()) -- num of weeks
GROUP BY datepart(year, Date), datepart(week, Date)
)
SELECT ROW_NUMBER() OVER(ORDER BY [year] DESC, WeekNum DESC) AS WeekNum, counts
FROM cte;
EDIT:
"yes, like if today is wednesday, 20 week will give you a week starting in wednesday"
It could be handled by:
WHERE Date >= DATEADD(week,-20,GETDATE())
=>
WHERE Date >= DATEADD(week,-20,
CAST(DATEADD(DAY, 1-DATEPART(WEEKDAY, GETDATE()), GETDATE()) AS DATE))
I'm trying to get the salary of an employee and I'm not sure why ISNULL is not working here. Although the same query works when used outside subquery.
Maybe I'm implementing the Isnull function wrong.
Net salary is showing null even there is basic pay given.
DECLARE #dateFrom datetime
SET #dateFrom = '2018-03-01'
DECLARE #dateTo datetime
SET #dateTo = '2018-03-31'
Select
[Emp].ID
,[Emp].EmpCode
,[Emp].FirstName + ' ' + [Emp].LastName AS Name
,[Emp].BasicPay
,((select SUM(InstallmentAmount) from HRM.tbl_EmployeeLoanInstallment [Loan]
LEFT JOIN HRM.tbl_EmployeeLoan [EmpLoan] ON [EmpLoan].ID = [Loan].EmployeeLoanCode
where [EmpLoan].EmpCode = Emp.ID AND IsReceived != 1
AND CONVERT(date, [Loan].InstallmentDueOn) >= CONVERT(date, #dateFrom)
AND CONVERT(date, [Loan].InstallmentDueOn) <= CONVERT(date, #dateTo))) AS LoanDeduction
,( SELECT
(ISNULL([Info].[BasicPay], 0))
-
(SELECT SUM(ISNULL([Loan].InstallmentAmount, 0))
FROM [HRM].[tbl_EmployeeLoan] [EmpLoan]
FULL JOIN [HRM].[tbl_EmployeeInfo] [Info] ON [Info].[ID] = [EmpLoan].[EmpCode]
FULL JOIN [HRM].[tbl_EmployeeLoanInstallment] [Loan] ON [EmpLoan].[ID] = [Loan].[EmployeeLoanCode]
WHERE
CONVERT(date, [Loan].InstallmentDueOn) >= CONVERT(date, #dateFrom)
AND
CONVERT(date, [Loan].InstallmentDueOn) <= CONVERT(date, #dateTo)
AND
[Info].[ID] = [Emp].[ID]
GROUP BY Info.ID)
FROM
[HRM].[tbl_EmployeeInfo] [Info]
WHERE Info.ID = Emp.ID
GROUP BY [Info].[ID], [Info].[BasicPay]
) AS NetSalary
from HRM.tbl_EmployeeInfo [Emp]
Output:
412 C3-345 Ayesha Fatima 20000.00 NULL NULL
413 C3-651 Zainab Ali 20000.00 NULL NULL
414 C1343 Ahmed Abdullah 20000.00 11111.11 8888.89
415 231 Ahmed Aslam 20000.00 NULL NULL
416 FS-16 Fawaz Aslam 25000.00 NULL NULL
Are you sure that your subquery return a value? Seems to not be the case.
If you really want a value, you should catch your null value at the end of your subquery.
,ISNULL(
( SELECT
(ISNULL([Info].[BasicPay], 0))
-
(SELECT SUM(ISNULL([Loan].InstallmentAmount, 0))
FROM [HRM].[tbl_EmployeeLoan] [EmpLoan]
FULL JOIN [HRM].[tbl_EmployeeInfo] [Info] ON [Info].[ID] = [EmpLoan].[EmpCode]
FULL JOIN [HRM].[tbl_EmployeeLoanInstallment] [Loan] ON [EmpLoan].[ID] = [Loan].[EmployeeLoanCode]
WHERE
CONVERT(date, [Loan].InstallmentDueOn) >= CONVERT(date, #dateFrom)
AND
CONVERT(date, [Loan].InstallmentDueOn) <= CONVERT(date, #dateTo)
AND
[Info].[ID] = [Emp].[ID]
GROUP BY Info.ID)
, 0)
FROM
[HRM].[tbl_EmployeeInfo] [Info]
I have a simple table which records people clocking-in and clocking out like so.
Id | EmployeeNumber | InOutId | InOutDateTime
-----------------------------------------------------
1 | 505 | IN | 2015-03-24 08:32:42:000
1 | 506 | IN | 2015-03-24 08:35:47:000
1 | 507 | IN | 2015-03-24 08:46:12:000
1 | 505 | OUT | 2015-03-24 16:59:00:000
1 | 506 | OUT | 2015-03-24 17:05:00:000
I want to show the total people currently IN and those currently OUT. In other words:
- Total IN means those that do not have a corresponding OUT for that given day. - Total OUT means those that do have an IN and an OUT for that given day.
So, based on my table above, I want to get the following results:
TotalCurrentlyIn | TotalCurrentlyOut
-----------------------------------------
1 | 2
This is what I have so far:
DECLARE #d date;
set #d = cast('2015-03-24 15:02:42.000' as date)
select EmployeeNumber, InOutId, InOutDateTime from MyAttendance
where
InOutDateTime >= DATEADD(day, DATEDIFF(day, 0, #d), 0)
and InOutDateTime < DATEADD(day, DATEDIFF(day, 0, #d) +1, 0)
order by
EmployeeNumber, InOutId
I need to be able to sum and group by - any ideas?
try,
DECLARE #d date;
set #d = cast('2015-03-24 15:02:42.000' as date)
;with cte as(
select t.EmployeeNumber,t.InOutId as in1,
t1.InOutId out1,t.InOutDateTime from #t t
left join (select EmployeeNumber,InOutId,InOutDateTime from #t
where InOutId='OUT' and cast(InOutDateTime as date)=cast(#d as date) ) t1
on t.EmployeeNumber=t1.EmployeeNumber and
cast(t.InOutDateTime as date)=cast(t1.InOutDateTime as date)
where t.InOutId='IN' and cast(t.InOutDateTime as date)=cast(#d as date))
select count(in1) Totalin,count(out1) Totalout, sum(case when out1 is null then 1 else 0 end) TotalCurrentlyIn
,count(out1) TotalCurrentlyOut from cte
data
declare #t table (Id int,EmployeeNumber int, InOutId varchar(3), InOutDateTime datetime)
insert into #t(Id, EmployeeNumber,InOutId, InOutDateTime) values
(1 , 505 , 'IN' , '2015-03-24 08:32:42:000'),
(1 , 506 , 'IN' , '2015-03-24 08:35:47:000'),
(1 , 507 , 'IN' , '2015-03-24 08:46:12:000'),
(1 , 505 , 'OUT' , '2015-03-24 16:59:00:000'),
(1 , 506 , 'OUT' , '2015-03-24 17:05:00:000')
CheckIn = 1 and CheckOut = 2 so you need to check last entry of all the uses.
Select EmployeeId, ActionType, Max(ActionDateTime)
From AttendanceLog
Where
ActionDateTime >= DATEADD(day, DATEDIFF(day, 0, #d), 0)
and ActionDateTime < DATEADD(day, DATEDIFF(day, 0, #d) +1, 0)
Group by
EmployeeId, ActionType
Order by
EmployeeId,ActionType
If I understand the question. you need to know how much person is in the office right now:
the first query return the max date for any employee, than you join it with the actionType
select
EmployeeId , max(ActionDateTime) as MaxActionDateTime into #temptable
from table
group by EmployeeId
select count (EmployeeId), ActionType
from table inner join #temptable
on table.EmployerId == #temptable.EmployerId
and table.ActionDateTime == #temptable.MaxActionDateTime
group by ActionType
Using a windowing function you can get the last action for every employee and count those
With data As (
Select id, EmployeeNumber, InOutId
, lastAction = ROW_NUMBER() OVER (PARTITION BY EmployeeNumber
ORDER BY InOutDateTime DESC)
From table1
)
Select Count(CASE InOutId WHEN 'IN' THEN 1 END) TotalCurrentlyIn
, Count(CASE InOutId WHEN 'OUT' THEN 1 END) TotalCurrentlyOut
From data
Where lastAction = 1
I have little table which gives me a very hard time:
Person datetime1 datetime2
Eric 2012-10-01 09:00:05.000 2012-10-01 22:00:00.000
Anna 2012-10-02 06:00:05.000 2012-10-03 12:00:05.000
Richard 2012-10-03 09:00:05.000 2012-10-04 02:00:05.000
Chuck 2012-10-01 12:00:05.000 2012-10-01 23:00:05.000
I am trying to write a query, which gives me statistics table. This table contains information about when a user logged in and out (daily granularity):
Date logged_in logged_off
2012-10-01 2 2
2012-10-02 1 0
2012-10-03 1 1
2012-10-04 0 1
According to my research, a pivot command could solve the problem?
select Person,
SUM(case when datetime1 = '2012-10-01' then 1 else 0 end) as [loggeed_in],
SUM(case when datetime2 = '2012-10-01' then 1 else 0 end) as [logged_of]
from table
group by Person
This is not working... Do you have any ideas?
This will fix the current query, but don't know if it will solve the whole problem...
select Person,
SUM(case when convert(varchar(10), datetime1, 111) = '2012/10/01' then 1 else 0 end) as [loggeed_in],
SUM(case when convert(varchar(10), datetime2, 111) = '2012/10/01' then 1 else 0 end) as [logged_of]
from table
group by Person
EDIT: I believe this will better suit requirements...
SELECT
[Date] = dt,
logged_in = (
SELECT COUNT(*)
FROM table1
WHERE convert(varchar(10), datetime1, 111) = convert(varchar(10), dt, 111)),
logged_off = (
SELECT COUNT(*)
FROM table1
WHERE convert(varchar(10), datetime2, 111) = convert(varchar(10), dt, 111))
FROM (
SELECT TOP 1000
row_number() OVER(ORDER BY (SELECT 0)) AS N
FROM master.dbo.syscolumns sc1, master.dbo.syscolumns sc2) tally
CROSS APPLY(
SELECT dt = DATEADD(dd, tally.N - 1, '2012-10-1')) tallydt
WHERE dt BETWEEN (SELECT MIN(dateadd(dd, -1, datetime1)) FROM table1) AND (SELECT MAX(datetime2) FROM table1)
GROUP BY dt
ORDER BY dt
Here is the working solution:
WITH O AS (
SELECT
CAST([login Date & Time] AS DATE) loginDate
,COUNT(*) logined
FROM table
GROUP BY CAST([login Date & Time] AS DATE)
), C AS (
SELECT
CAST([Close Date & Time] AS DATE) CloseDate
,COUNT(*) Closed
FROM table
WHERE [Close Date & Time] IS NOT NULL
GROUP BY CAST([Close Date & Time] AS DATE)
)
SELECT
COALESCE(C.CloseDate, O.loginDate) TheDate
--,O.loginDate
--,C.CloseDate
,O.logined
,C.Closed
FROM O
FULL JOIN C
ON O.loginDate = C.CloseDate
ORDER BY TheDate