How to get the MAX() while NOT grouping all columns in SQL Server - sql

I have this query.
The INNER SELECT brings back multiple records. The outer does a SUM & MAX so I only have 1 record:
SELECT z.EmployeeId,
SUM(z.PayrollGap) AS PayrollGap,
MAX(z.PayrollGap) AS PayrollGapMax
FROM (SELECT DISTINCT
a.EmployeeId,
a.PayPeriodStart,
a.PayPeriodEnd,
b.PayPeriodStart AS NextStartDate,
CASE WHEN DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1 < 0 THEN 0
ELSE DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1
END AS PayrollGap
FROM EmployeePayroll a
LEFT JOIN EmployeePayroll b
ON b.EmployeeId = a.EmployeeId
AND b.rn = a.rn + 1
WHERE b.PayPeriodStart IS NOT NULL) z
GROUP BY z.EmployeeId
Along with the MAX(z.PayrollGap), I need to grab the PayPeriodStart as well.
The problem is that if I add the column PayPeriodStart to the query, it'll bring back more than 1 record and I need to do a MAX(z.PayrollGap).
How do I go about running this query but at the same time bringing back the PayPeriodStart RELATED TO MAX(z.PayrollGap)?

Try to split query:
;with cte as
(
SELECT DISTINCT
a.EmployeeId,
a.PayPeriodStart,
a.PayPeriodEnd,
b.PayPeriodStart AS NextStartDate,
CASE WHEN DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1 < 0 THEN 0
ELSE DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1
END AS PayrollGap
FROM EmployeePayroll a
LEFT JOIN EmployeePayroll b
ON b.EmployeeId = a.EmployeeId
AND b.rn = a.rn + 1
WHERE b.PayPeriodStart IS NOT NULL
),
res as
(
SELECT z.EmployeeId,
SUM(z.PayrollGap) AS PayrollGap,
MAX(z.PayrollGap) AS PayrollGapMax
FROM cte z
GROUP BY z.EmployeeId
)
select r.EmployeeId, r.PayrollGap, r.PayrollGapMax, c.PayPeriodStart
from res r
join cte c on c.EmployeeId = r.EmployeeId
and c.PayrollGap = r.PayrollGapMax

If I understand the question correctly you need to join your result set back to EmployeePayroll to add in PayPeriodStart.
Something like:
WITH cte AS (
SELECT DISTINCT
a.EmployeeId,
a.PayPeriodStart,
a.PayPeriodEnd,
b.PayPeriodStart AS NextStartDate,
CASE WHEN DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1 < 0 THEN 0
ELSE DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1
END AS PayrollGap
FROM EmployeePayroll a
LEFT JOIN EmployeePayroll b
ON b.EmployeeId = a.EmployeeId
AND b.rn = a.rn + 1
WHERE b.PayPeriodStart IS NOT NULL
)
SELECT EmployeeId
,PayrollGap
,PayrollGapMax
,PayPeriodStart
FROM (SELECT z.EmployeeId,
SUM(z.PayrollGap) AS PayrollGap,
MAX(z.PayrollGap) AS PayrollGapMax
FROM cte z
GROUP BY z.EmployeeId) x
INNER JOIN
cte ON cte.EmployeeId = x.EmployeeId
AND cte.PayrollGap = x.PayrollGapMax
Which isn't optimized.
Or tested, since no sample data.
Or formatted particularly nicely.

Try:
select distinct A.EmployeeId, A.PayrollGap, A.PayrollGapMax, B.PayPeriodStart
(SELECT z.EmployeeId,
SUM(z.PayrollGap) AS PayrollGap,
MAX(z.PayrollGap) AS PayrollGapMax
FROM (SELECT DISTINCT
a.EmployeeId,
a.PayPeriodStart,
a.PayPeriodEnd,
b.PayPeriodStart AS NextStartDate,
CASE WHEN DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1 < 0 THEN 0
ELSE DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1
END AS PayrollGap
FROM EmployeePayroll a
LEFT JOIN EmployeePayroll b
ON b.EmployeeId = a.EmployeeId
AND b.rn = a.rn + 1
WHERE b.PayPeriodStart IS NOT NULL) z
GROUP BY z.EmployeeId) A
inner join
(SELECT DISTINCT
a.EmployeeId,
a.PayPeriodStart,
a.PayPeriodEnd,
b.PayPeriodStart AS NextStartDate,
CASE WHEN DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1 < 0 THEN 0
ELSE DATEDIFF(d, a.PayPeriodEnd, b.PayPeriodStart) - 1
END AS PayrollGap
FROM EmployeePayroll a
LEFT JOIN EmployeePayroll b
ON b.EmployeeId = a.EmployeeId
AND b.rn = a.rn + 1
WHERE b.PayPeriodStart IS NOT NULL) B
ON A.EmployeeId=B.EmployeeId and B.PayrollGap=A.PayrollGapMax

Related

Subquery returned more than 1 value no clue where the issue is?

SELECT MAX(te.StoreID) AS StoreID,
SUM(te.Price * te.Quantity) AS Sales,
SUM(te.Cost * te.Quantity) AS Cost,
COUNT(DISTINCT t.TransactionNumber) AS Trxn,
SUM(te.Quantity) AS Quantity
FROM TransactionEntry te
INNER JOIN [Transaction] t
ON te.TransactionNumber = t.TransactionNumber
AND te.StoreID = t.StoreID
LEFT JOIN item i
ON te.itemID = i.ID
LEFT JOIN Department d
ON i.DepartmentID = d.ID
WHERE d.ID <> 8 AND DATEDIFF(day, t.Time, GETDATE()) = 1
AND t.WebInvoiceID <> (select WebInvoiceID from [Transaction] where WebInvoiceID>0)
GROUP BY te.StoreID
Can anyone help me with this?
The error is in this line:
AND t.WebInvoiceID <> (select WebInvoiceID from [Transaction] where WebInvoiceID > 0 )
One way to fix this is to use NOT IN since the subquery returns multiple rows.
AND t.WebInvoiceID NOT IN (select WebInvoiceID from [Transaction] where WebInvoiceID>0)
Another way is by using NOT EXISTS which I preferred more
WHERE d.ID <> 8 AND DATEDIFF(day, t.Time, GETDATE()) = 1
AND NOT EXISTS
(
SELECT 1
FROM [Transaction] tr
WHERE t.WebInvoiceID = tr.WebInvoiceID
AND tr.WebInvoiceID > 0
)
If not mistaken, based from your original logic, you don't a subquery to filter out WebInvoiceID which are greater than zero. This can be simplified as:
WHERE d.ID <> 8 AND DATEDIFF(day, t.Time, GETDATE()) = 1
AND t.WebInvoiceID > 0

SQL Sub-query Error

I think there is issue with the A2 set where I am only including Restart_case = Y, but I am not sure please help. I am getting this error: An unexpected token "WHERE" was found following "_ID) AS Max_DateBegin". Expected tokens may include...
Thank you
SELECT A2.* FROM
(SELECT A1.*, min(BeginDate) OVER (PARTITION BY Per_ID) AS Min_BeginDate,
MAX(BeginDate) OVER (PARTITION BY Per_ID) AS Max_BeginDate
WHERE RestartCase = 'Y'
From) A2
(SELECT distinct C.Per_ID, P.DOB, C.BeginDate, C.EndDate, C.RestartCase, P.per_type
FROM CaseSum C LEFT JOIN PERSON p on C.ID_PRSN = P.ID_PRSN) A1
WHERE per_Type = 1 AND BeginDate <= '9/30/2017' AND (EndDate >= '10/01/2017' OR EndDate IS NULL)
ORDER BY A1.Per_ID
Consider a conditional CASE inline aggregate in your window functions to combine both attempted resultsets:
SELECT DISTINCT C.Per_ID, P.DOB, C.BeginDate, C.EndDate, C.RestartCase, P.per_type,
MIN(CASE WHEN C.RestartCase = 'Y' THEN C.BeginDate END)
OVER (PARTITION BY C.Per_ID) AS Min_BeginDate,
MAX(CASE WHEN C.RestartCase = 'Y' THEN C.BeginDate END)
OVER (PARTITION BY C.Per_ID) AS Max_BeginDate
FROM CaseSum C
LEFT JOIN PERSON P on C.ID_PRSN = P.ID_PRSN
WHERE P.per_Type = 1 AND C.BeginDate <= '9/30/2017'
AND (C.EndDate >= '10/01/2017' OR C.EndDate IS NULL)
ORDER BY C.Per_ID
I believe this is what you want (or something like it):
SELECT
A2.*
FROM
(SELECT
A1.*,
MIN(BeginDate) OVER (PARTITION BY Per_ID) AS Min_BeginDate,
MAX(BeginDate) OVER (PARTITION BY Per_ID) AS Max_BeginDate
FROM
(SELECT DISTINCT
C.Per_ID,
P.DOB,
C.BeginDate,
C.EndDate,
C.RestartCase,
P.per_type
FROM CaseSum C
LEFT JOIN PERSON P
on C.ID_PRSN = P.ID_PRSN
WHERE P.per_Type = 1
AND C.BeginDate <= '9/30/2017'
AND (C.EndDate >= '10/01/2017' OR C.EndDate IS NULL)
) A1
WHERE A1.RestartCase = 'Y'
ORDER BY A1.Per_ID
) A2

Union Order By using different field SQL Server

Im trying to sort below query by using Order By. However when im using MonthYear field the output result display as below which not sort accordingly by latest year and month.
Order By MonthYear Output.
MonthYear
2017/9
2017/8
2017/7
2017/6
2017/5
2017/4
2017/10
So I want to try by order by year and month but i cant do it since another query after union all line has different field which is a.created_year. Is there any way I can do it?
SELECT CONVERT(varchar, A.YEAR) + '/' + CONVERT(varchar, A.MONTH)AS MonthYear
FROM PAY_Previous AS A LEFT OUTER JOIN
EMPLOYEE_BADGE AS B ON A.EMPLOYEE_NO = B.EMPLOYEE_NO LEFT OUTER JOIN
V_EMPLOYEE_PROFILES AS C ON A.EMPLOYEE_NO = C.EMPLOYEE_NO
WHERE A.EMPLOYEE_NO = '710049' AND A.FREQUENCY = 2 and (day(getdate())>=28 or month(getdate()) > a.Month or YEAR(getdate()) > A.YEAR )
UNION ALL-
SELECT CONVERT(varchar, YEAR(A.CREATED_DATE)) + '/' + CONVERT(varchar, A.MONTH) AS MonthYear
FROM PAY_Current AS A LEFT OUTER JOIN
EMPLOYEE_BADGE AS B ON A.EMPLOYEE_NO = B.EMPLOYEE_NO LEFT OUTER JOIN
V_EMPLOYEE_PROFILES AS C ON A.EMPLOYEE_NO = C.EMPLOYEE_NO
WHERE A.EMPLOYEE_NO = '710049'AND A.FREQUENCY = 2 and (day(getdate())>=28 or month(getdate()) > a.Month or YEAR(getdate()) > A.CREATED_DATE )
ORDER BY YEAR(A.CREATED_DATE) DESC ,A.MONTH DESC
Expected Output
MonthYear
2017/10
2017/9
2017/8
2017/7
2017/6
2017/5
2017/4
Leave the data numeric until the last moment, then convert just the smallest list for presentation, and this leave the numeric columns available for ordering the result.
SELECT CONVERT(varchar, d.[YEAR]) + '/' + CONVERT(varchar, d.[MONTH]) AS MonthYear
FROM
SELECT
A.[YEAR] , A.[MONTH]
FROM PAY_Previous AS A
LEFT OUTER JOIN EMPLOYEE_BADGE AS B ON A.EMPLOYEE_NO = B.EMPLOYEE_NO
LEFT OUTER JOIN V_EMPLOYEE_PROFILES AS C ON A.EMPLOYEE_NO = C.EMPLOYEE_NO
WHERE A.EMPLOYEE_NO = '710049'
AND A.FREQUENCY = 2
AND (DAY(GETDATE()) >= 28
OR MONTH(GETDATE()) > a.Month
OR YEAR(GETDATE()) > A.YEAR)
UNION
SELECT
YEAR(A.CREATED_DATE) [Year] , A.[MONTH]
FROM PAY_Current AS A
LEFT OUTER JOIN EMPLOYEE_BADGE AS B ON A.EMPLOYEE_NO = B.EMPLOYEE_NO
LEFT OUTER JOIN V_EMPLOYEE_PROFILES AS C ON A.EMPLOYEE_NO = C.EMPLOYEE_NO
WHERE A.EMPLOYEE_NO = '710049'
AND A.FREQUENCY = 2
AND (DAY(GETDATE()) >= 28
OR MONTH(GETDATE()) > a.Month
OR YEAR(GETDATE()) > A.CREATED_DATE)
) d
ORDER BY d.[YEAR] DESC, d.[MONTH] DESC
btw: Both "YEAR" and "MONTH" are terms used by T-SQL and it is not a good idea to use them as columns names.
IF, you need the result in a form that can reliably be re-ordered, then perhaps you could consider using a leading zero for the month number. e.g.
CONVERT(varchar(4), d.[YEAR]) + '/' + RIGHT('0' + CONVERT(varchar(2), d.[MONTH]),2) AS MonthYear
Which would look like this (and sort correctly)
2017/10
2017/04
2017/03
2017/02
2017/01
2016/10
2016/05
You are probably having a sort problem because you are using Created_Date to sort, but you are using different fields for MonthYear between statements.
Try this:
select MonthYear from (
SELECT CONVERT(varchar, A.YEAR) + '/' + CONVERT(varchar, A.MONTH)AS MonthYear, A.YEAR yr, A.MONTH mon
FROM PAY_Previous AS A LEFT OUTER JOIN
EMPLOYEE_BADGE AS B ON A.EMPLOYEE_NO = B.EMPLOYEE_NO LEFT OUTER JOIN
V_EMPLOYEE_PROFILES AS C ON A.EMPLOYEE_NO = C.EMPLOYEE_NO
WHERE A.EMPLOYEE_NO = '710049' AND A.FREQUENCY = 2 and (day(getdate())>=28 or month(getdate()) > a.Month or YEAR(getdate()) > A.YEAR )
UNION ALL
SELECT CONVERT(varchar, YEAR(A.CREATED_DATE)) + '/' + CONVERT(varchar, A.MONTH) AS MonthYear. YEAR(A.CREATED_DATE)) yr, CONVERT(varchar, A.MONTH) mon
FROM PAY_Current AS A LEFT OUTER JOIN
EMPLOYEE_BADGE AS B ON A.EMPLOYEE_NO = B.EMPLOYEE_NO LEFT OUTER JOIN
V_EMPLOYEE_PROFILES AS C ON A.EMPLOYEE_NO = C.EMPLOYEE_NO
WHERE A.EMPLOYEE_NO = '710049'AND A.FREQUENCY = 2 and (day(getdate())>=28 or month(getdate()) > a.Month or YEAR(getdate()) > A.CREATED_DATE )
)x
ORDER BY yr DESC, mon DESC

Select inside CASE THEN

I need to select the project rate or shift rate that has the effective date less than today.
SELECT
CASE
WHEN ISNULL(s.rate,0) = 0
THEN SELECT TOP 1 pr.rate FROM ProjectRates pr WHERE (pr.projectID = p.ID) AND (pr.effectiveDate < GETDATE()) ORDER BY pr.effectiveDate DESC
--p.rate
ELSE SELECT TOP 1 sr.rate FROM ShiftRates sr WHERE (sr.shiftID = s.ID) AND (sr.effectiveDate < GETDATE()) ORDER BY pr.effectiveDate DESC
--s.rate
END AS rate
FROM Projects p
INNER JOIN Shifts s ON (p.ID = s.projectID)
WHERE (p.ID = #projectID)
Please note that this code snippet is part of a larger stored proc and thus it must be within a CASE statement.
Subqueries need parentheses:
SELECT (CASE WHEN ISNULL(s.rate, 0) = 0
THEN (SELECT TOP 1 pr.rate
FROM ProjectRates pr
WHERE (pr.projectID = p.ID) AND (pr.effectiveDate < GETDATE())
ORDER BY pr.effectiveDate DESC
)
ELSE (SELECT TOP 1 sr.rate
FROM ShiftRates sr
WHERE (sr.shiftID = s.ID) AND (sr.effectiveDate < GETDATE())
ORDER BY pr.effectiveDate DESC
) --s.rate
END) AS rate
FROM Projects p INNER JOIN
Shifts s
ON p.ID = s.projectID
WHERE p.ID = #projectID;

Is this possible to simplify this SQL query?

I have designed the following sql query to get the percentage of
100 - ((reportedDate – Submission Date) / TotalNumOfVisits) * 100
Is there any way to simplify it? Like combine the two queries into one?
SELECT
q1.VisitMonth,q1.TotalVisit, ISNULL(q2.diff,0) AS DIFF
,100 - ISNULL( (CAST((q2.diff * 1.0 / q1.TotalVisit ) * 100 AS FLOAT)),0) PERC
FROM
(
SELECT DATENAME(MONTH,v.VisitDate) as VisitMonth, count(v.VisitID) as TotalVisit
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by DATENAME(MONTH,v.VisitDate)
) q1
LEFT OUTER JOIN
(
SELECT DATENAME(MONTH,v.VisitDate) as MonthName,COUNT(*) as diff
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
AND DATEDIFF(DAY,v.ReportDate,v.SubmissionDate) > 2
group by DATENAME(MONTH,v.VisitDate)
) q2
ON q1.VisitMonth = q2.MonthName
Result:
Try this :-
Select
VisitMonth,isnull(diff,0) as DIFF,
100 - ISNULL( (CAST((diff * 1.0 / Nullif(TotalVisit,0) ) * 100 AS FLOAT)),0) PERC
from
(
SELECT
VisitMonth = Datename(month,visitDate) ,
Diff = Sum(case when DATEDIFF(DAY,v.ReportDate,v.SubmissionDate) > 2 then 1
else 0 end) ,
TotalVisit = Count(v.VisitID)
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by DATENAME(MONTH,v.VisitDate)
)a