Combine Three Grouped Select Queries Into One - sql

--ON TIME PMWO's
SELECT LOCATION, COUNT(WONUM) AS OnTimePMWOs
FROM
WORKORDER
WHERE worktype = 'pm' and actfinish<=targcompdate
GROUP BY LOCATION
--PAST DUE PMWO'S
SELECT LOCATION, COUNT(WONUM) AS PastDuePMWOs
FROM
WORKORDER
WHERE worktype = 'pm' and actfinish>=targcompdate
GROUP BY LOCATION
--30 DayForecast-
SELECT W.location, COUNT(W.wonum) AS Forecast30days
from
workorder AS W
INNER JOIN PMFORECAST AS P
ON W.CHANGEDATE=P.CHANGEDATE
WHERE WORKTYPE='PM' AND P.forecastdate>= GETDATE()+30
GROUP BY LOCATION

This is an answer (all be it just a guess based on the limited question). I'm not a fan of placing CASE statements inside aggregates, but depending on environment, indexing, and data in the included tables, it might perform okay. Please be more involved when posting things. Show us what you've tried, explain the problem, give samples of data, include the desired output, all that fun stuff. The better the question, the better chance of a good answer. Okay, done on the high horse...
DECLARE #Forecast30days DATE
SET #Forecast30days = CURRENT_TIMESTAMP + 30
SELECT
wo.LOCATION
,COUNT(CASE WHEN wo.actfinish <= wo.targcompdate THEN wo.WONUM ELSE NULL END) AS OnTimePMWOs
,COUNT(CASE WHEN wo.actfinish >= wo.targcompdate THEN wo.WONUM ELSE NULL END) AS PastDuePMWOs
,COUNT(CASE WHEN pm.changedate IS NOT NULL THEN wo.WONUM ELSE NULL END) AS Forecast30days
FROM WORKORDER AS wo
LEFT JOIN PMFORECAST AS pm
ON wo.changedate = pm.changedate
AND pm.forecastdate >= #Forecast30days
WHERE wo.worktype = 'pm'
AND ((wo.actfinish IS NOT NULL AND wo.targcompdate IS NOT NULL)
OR (pm.changedate IS NOT NULL))
GROUP BY
wo.LOCATION

Related

Trying to strip out values from a column based on another columns and store that value to be used in a calculation

I am somewhat a novice at t-sql. I'm having trouble getting incorporating row values into a variables based on another columns value.
The database table has a single quantity field where all the quantity amounts are stored as positive amounts. There is a separate column that represents the direction of the transaction. A direction of 1 means it's inbound. A direction of -1 means it's inbound. Netting the inbound qty against the outbound provides the net remaining quantity.
I've been able to segregate inbound and outbound into separate columns. I've aliased each column (InQty and OutQty) to give the reader the proper context. The last thing I need is to net the two amounts (i.e. InQty minus OutQty) into another column labelled NetQty. I've tried to use variables, with and without a case statement but nothing I've tried is working. I fell like I'm real close but just can't seem to get the proper structure to the query.
The output I'm trying to get is:
ItemID ItemPartNumber InQty OutQty NetQty
1140797106360931 ACRFCO 5.000000 2.000000
Below is the query I've developed so far.
The comment lines are there to me assist in developing the query.
Select convert(varchar(max), i.itemid) as ItemID
,ItemPartNumber As ItemPartNumber
,Sum(case when Direction = 1 Then TransactionQty end) As InQty
,Sum(case when Direction = -1 Then TransactionQty end) as OutQty
FROM InventoryTransaction IT
join item i on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
Order by it.ItemPartNumber
I think you want:
select i.itemid, i.ItemPartNumber,
Sum(case when Direction = 1 Then TransactionQty end) As InQty,
Sum(case when Direction = -1 Then TransactionQty end) as OutQty,
sum(case when Direction = 1 then TransactionQty
when Direction = -1 then TransactionQty
end) as netqty
from InventoryTransaction IT i
item i
on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
order by it.ItemPartNumber;
I see no reason to convert the first value to a string, so I removed the conversion.
Assuming that direction only takes on the values -1 and 1 (and perhaps 0 and NULL), you can simplify this to:
select i.itemid, i.ItemPartNumber,
sum(case when Direction = 1 Then TransactionQty end) As InQty,
sum(case when Direction = -1 Then TransactionQty end) as OutQty,
sum(Direction * TransactionQty) as netqty
from InventoryTransaction IT i
item i
on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
order by it.ItemPartNumber
I think a good way to avoid further comparisons, sums or other operations and get better performance could be to use your query as a subquery:
SELECT ItemID, ItemPartNumber, InQty, OutQty, InQty-OutQty AS NetQty FROM(
/* Start of your query */
Select convert(varchar(max), i.itemid) as ItemID
,ItemPartNumber As ItemPartNumber
,Sum(case when Direction = 1 Then TransactionQty end) As InQty
,Sum(case when Direction = -1 Then TransactionQty end) as OutQty
FROM InventoryTransaction IT
join item i on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
Order by it.ItemPartNumber
/* End of your query */
) AS T;

Alternative approach to multiple left joins

I have the following queries to count sales by route
SELECT DISTINCT q.sales_route,
y.yesterday,
t.today
FROM tblquotesnew q
left join (SELECT tblquotesnew.sales_route,
Count(tblquotesnew.sales_route) AS Yesterday
FROM tblquotesnew
WHERE tblquotesnew.date_sent_to_registrations =
Trunc(SYSDATE - 1)
AND sales_route IS NOT NULL
GROUP BY tblquotesnew.sales_route) y
ON q.sales_route = y.sales_route
left join (SELECT tblquotesnew.sales_route,
Count(tblquotesnew.sales_route) AS Today
FROM tblquotesnew
WHERE tblquotesnew.date_sent_to_registrations =
Trunc(SYSDATE)
AND sales_route IS NOT NULL
GROUP BY tblquotesnew.sales_route) t
ON q.sales_route = t.sales_route
I then have 6 other left joins to count current and previous week, month, and year.
This approach does work, but I was wondering if this is a more efficient (in terms of lines of code) way of pulling together this data?
I think you just need conditional aggregation:
select q.sales_route,
sum(case when q.date_sent_to_registrations = trunc(SYSDATE - 1)
then 1 else 0
end) as yesterday,
sum(case when q.date_sent_to_registrations = trunc(SYSDATE)
then 1 else 0
end) as today
from tblquotesnew q
group by sales_route
You may use conditional aggregation
SELECT sales_route,
sum(CASE WHEN date_sent_to_registrations = Trunc(SYSDATE)
AND sales_route IS NOT NULL
THEN 1 ELSE 0 END) today,
sum(CASE WHEN date_sent_to_registrations = Trunc(SYSDATE - 1)
AND sales_route IS NOT NULL
THEN 1 ELSE 0 END) yesterday
FROM tblquotesnew
GROUP BY sales_route
conditional aggregation leads to one sequential scan of your table which may be ok in many cases. An alternative solution is to use subqueries behind SELECT which may be sometimes more efficient. For example, if you access small subselect of data and you can create indexes to support it.

SQL - Inserting a condition in a GROUP BY

My issue is that some of the records in the result set are excluded because they are missing a Min_Date or Max_Date or Both. I need these records to be included so that I can show the runner ran in the race even if he did not reach a First, Last or any checkpoint. Any direction is appreciated.
SELECT A.Date, A.RunnerName, A.Duplicates, A.TotalWaypointsReached,
B.FirstWaypoint, C.LastWaypoint, C.rDateTime as MostRecent
FROM (
SELECT RunnerName,
CONVERT(NVARCHAR(25), rDatetime, 101) AS Date,
Min(case when FirstWaypoint is null OR FirstWaypoint = '' then null else rDateTime end) MIN_DATE,
Max(case when LastWaypoint is null OR LastWaypoint = '' then null else rDateTime end) MAX_DATE,
--IF(I'm missing a Max_Date, Min_Date, or both after all records in a group. Add Max(rDateTime) and Min(rDateTime))
Count(*) AS Duplicates,
SUM(TotalWaypoints) as TotalWaypointsReached
FROM Race A
GROUP BY RunnerName, CONVERT(NVARCHAR(25), rDateTime, 101)
HAVING Count(*) > 1 ) A
LEFT JOIN Race B
on A.RunnerName = B.RunnerName
and A.MIN_DATE = B.rDateTime
LEFT JOIN Race C
on A.RunnerName = C.RunnerName
and A.MAX_DATE = C.rDatetime
I'm using the select statement via SQL command in Visual Studio 2008.

SQL Sum MTD & YTD

I have only just started looking into SQL.
I have a SQL Server 2008 r2 database that will return two fields DocDate & InvValue. I need to sum the InvValues as MTD & YTD as of Today's Date So it looks like
**Period** /////// **Total value**
MTD ////////////111111.11
YTD /////////////999999.99
I have done a fair amount of Googling and can do one or the other with SUM & DATEPART, but I am stuck with trying to do both.
Can someone give me some pseudo-code that would help me google a little further.
Thank you #Gordon Linoff, That helped a lot and I learned something, That I will find useful in the future.
My code now looks like:
SELECT
SUM(CASE WHEN YEAR(T1.[DocDate]) = YEAR(GETDATE()) THEN T0.[TotalSumSy] END) AS YTD,
SUM(CASE WHEN YEAR(T1.[DocDate]) = YEAR(GETDATE()) AND MONTH(T1.[DocDate]) = MONTH(GETDATE()) THEN T0.[TotalSumSy] END) AS MTD
FROM [dbo].[INV1] T0 INNER JOIN [dbo].[OINV] T1 ON T1.[DocEntry] = T0.[DocEntry]
However I now get
YTD.........MTD
99999.99....111111.11
And I need
YTD........99999.99
MTD........11111.11
Any further assistance would be appreciated.
You can do this with conditional aggregation:
select sum(case when year(docdate) = year(getdate()) then InvValue end) as YTD,
sum(case when year(docdate) = year(getdate()) and month(docdate) = month(getdaate())
then InvValue
end) as MTD
from table t;
This assumes you have no future dates in the table. If you do, add in docdate < getdate() to both clauses.
EDIT:
If you need this in two rows, you can simply do this:
select (case when n.n = 1 then 'YTD' else 'MTD' end) as which,
(case when n.n = 1 then YTD else MTD end) as value
from (select sum(case when year(docdate) = year(getdate()) then InvValue end) as YTD,
sum(case when year(docdate) = year(getdate()) and month(docdate) = month(getdaate())
then InvValue
end) as MTD
from table t
) cross join
(select 1 as n union all select 2) n;
SELECT
Period = 'MTD',
Total_value = SUM(T0.TotalSumSy)
FROM dbo.INV1 T0
INNER JOIN dbo.OINV T1
ON T1.DocEntry = T0.DocEntry
WHERE
T1.DocDate >= DATEADD(month,DATEDIFF(month,'20010101',GETDATE()),'20010101')
AND
T1.DocDate < DATEADD(month,1+DATEDIFF(month,'20010101',GETDATE()),'20010101')
UNION ALL
SELECT
'YTD',
SUM(T0.TotalSumSy)
FROM dbo.INV1 T0
INNER JOIN dbo.OINV T1
ON T1.DocEntry = T0.DocEntry
WHERE
T1.DocDate >= DATEADD(year,DATEDIFF(year,'20010101',GETDATE()),'20010101')
AND
T1.DocDate < DATEADD(year,1+DATEDIFF(year,'20010101',GETDATE()),'20010101') ;
The (complicated) conditions at the WHERE clauses are used instead of the YEAR(column) = YEAR(GETDATE() and the other you had previously, so indexes can be used. WHen you apply a function to a column, you make indexes unsuable (with some minor exceptions for some functions and some verios of SQL-Server.) So, the best thing is to try to convert the conditions to this type:
column <operator> AnyComplexFunction()

SQL Query to compare 2 weeks

I've got to design a query in visual studio where I have 2 data sets.
basically it goes like this.
I want to compare this weeks call total to last week per country calling.
the only thing is last weeks calls may have come from 20 diff countries while this weeks might only have come from 15.
How can I make the query such that the 20 countries will show up for both while having "0" value in for countries that do not appear this week.
below is my query:
Select country,
Sum(Case When actstatus in (5,105) Then 1 Else 0 End) As TotalCalls,
Sum(Case When actstatus = 105 Then 1 Else 0 End) As FailedCalls
From termactivity(nolock)
INNER JOIN termconfig(NOLOCK) ON cfgterminalID = actterminalID
INNER JOIN Country (nolock) on country = cycode
Where actstatus in (5,105)
and (actTerminalDateTime BETWEEN #StartDate-7 AND #EndDate-7)
Group By country
order By country asc
When Act status = 105 it means the call was not completed and when it = 5 it means the call was successful. I am doing this to get a successful call % rate per week.
Thanks in Advance!
Apply the same logic as you did to total calls and failed calls as you did to the this week and last week.
SELECT country,
COUNT(CASE WHEN actTerminalDateTime < #StartDate THEN 1 END) [LastWeekTotalCalls],
COUNT(CASE WHEN ActStatus = 105 AND actTerminalDateTime < #StartDate THEN 1 END) [LastWeekFailedCalls],
COUNT(CASE WHEN actTerminalDateTime >= #StartDate THEN 1 END) [ThisWeekTotalCalls],
COUNT(CASE WHEN ActStatus = 105 AND actTerminalDateTime >= #StartDate THEN 1 END) [ThisWeekFailedCalls]
FROM termactivity (NOLOCK)
INNER JOIN termconfig (NOLOCK)
ON cfgterminalID = actterminalID
INNER JOIN Country (NOLOCK)
ON country = cycode
WHERE actstatus in (5,105)
AND actTerminalDateTime BETWEEN DATEADD(DAY, -7, #StartDate) AND #EndDate
GROUP BY country
ORDER BY country ASC
I've also tidied up your query slightly, for example there is no point in specifying
WHEN ActStatus IN (5, 105) ...
When your WHERE clause already limits all results to 5, 105, therefore this is a redundant predicate in your case expression
From what I understand, you want to perform separate queries for two weeks, and you want both queries to produce rows for all countries, regardless of whether all countries had any calls. To achieve this, you need to use LEFT OUTER JOINS. The below code should guarantee that every country found in the Country table has a row, even if both sums are 0.
SELECT country,
SUM(CASE WHEN actstatus IN (5,105) THEN 1 ELSE 0 END) AS TotalCalls,
SUM(CASE WHEN actstatus = 105 THEN 1 ELSE 0 END) AS FailedCalls
FROM Country (NOLOCK)
LEFT OUTER JOIN termconfig (NOLOCK) ON country = cycode
LEFT OUTER JOIN termactivity (NOLOCK) ON cfgterminalID = actterminalID
WHERE (actTerminalDateTime BETWEEN #StartDate-7 AND #EndDate-7)
GROUP BY country
ORDER BY country ASC
If this was not what you wanted, perhaps you need to clarify your question. Many others have assumed that you want to combine the results into a single query.