sql join and group by generated date range - sql

I have Table1 and I need a query to populate Table2:
Problem here is with Date column. I want to know the process of location/partner combination per day. Main issue here is that I can't pick DateCreated and make it as default date since it doesn't necessarily cover whole date range, like in this example where it doesn't have 2015-01-07 and 2015-01-09. Same case with other dates.
So, my idea is to first select dates from some table which contains needed date range and then perform calculation for each day/location/partner combination from cte but in that case I can't figure out how to make a join for LocationId and PartnerId.
Columns:
Date - CreatedItems - number of created items where Table1.DateCreated = Table2.Date
DeliveredItems - number of delivered items where Table1.DateDateOut = Table2.Date
CycleTime - number of days delivered item was in the location (DateOut - DateIn + 1)
I started with something like this but it's very like that I completely missed the point with it:
with d as
(
select date from DimDate
where date between DATEADD(DAY, -365, getdate()) and getdate()
),
cr as -- created items
(
select
DateCreated,
LocationId,
PartnerId,
CreatedItems = count(*)
from Table1
where DateCreated is not null
group by DateCreated,
LocationId,
PartnerId
),
del as -- delivered items
(
select
DateOut,
LocationId,
ParnerId,
DeliveredItems = count(*),
CycleTime = DATEDIFF(Day, DateOut, DateIn)
from Table1
where DateOut is not null
and Datein is not null
group by DateOut,
LocationId,
PartnerId
)
select
d.Date
from d
LEFT OUTER JOIN cr on cr.DateCreated = d.Date -- MISSING JOIN PER LocationId and PartnerId
LEFT OUTER JOIN del on del.DateCompleted = d.Date -- MISSING JOIN PER LocationId and PartnerId

with range(days) as (
select 0 union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 /* extend as necessary */
)
select dateadd(day, r.days, t.DateCreated) as "Date", locationId, PartnerId,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.days between 0 and datediff(day, t.DateCreated, t.DateOut)
group by dateadd(day, r.days, t.DateCreated), LocationId, PartnerId;
If you only want the end dates (rather than all the dates in between) this is probably a better approach:
with range(dt) as (
select distinct DateCreated from T union
select distinct DateOut from T
)
select r.dt as "Date", locationId, PartnerId,
sum(
case
when r.dt = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when r.dt = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when r.dt = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.dt in (t.DateCreated, t.DateOut)
group by r.dt, LocationId, PartnerId;

If to specify WHERE clause? Something Like that:
WHERE cr.LocationId = del.LocationId AND
cr.PartnerId = del.PartnerId

Related

Why is a value not returned if another value is 0 in SQL query

I have a query that does not return the expense value if the drvalue(Debtor Value) is equal to 0 or NULL,
If i change the drvalue to any value greater than 0, query returns the expenses value.
Drvalue is a sum of all the values for a specific period
Below is the query
SELECT f.vehiclenumber,f.fleettype,f.IsCreditor,
ISNULL(SUM(l.DrValue),0) AS drvalue,
CASE WHEN iscreditor=1 then Sum (DrValue) - Sum(CrValue) else null end AS Profit,
sum(l.CrValue) AS CrValue ,
sum(l.distance) AS LoadDist,
sum(l.DrValue)/DDist AS DRVal ,
d.Liters AS Liters,d.ddist AS DDist, d.DDiesel AS DDiesel, d.DieselCost AS dieslCost,
(MAX(isnull(l.Closingkm,0))-MIN(isnull(l.OpeningKM,0))) AS [CO],ISNULL(SUM(l.DrValue),0) / CASE WHEN (isnull(MAX(l.Closingkm),0)-MIN(isnull(l.OpeningKM,0)))=0 THEN 1 ELSE (isnull(MAX(l.Closingkm),0)-MIN(isnull(l.OpeningKM,0))) END AS TotalCPK,
(d.DieselCost/sum(l.DrValue)) * 100 AS DieselPerc,
SUM(jobdetails.total) AS Expenses,
count(l.vehicleNo) AS LoadCount
FROM tblVehicle AS f
LEFT JOIN (SELECT Vehicleno, loaddate, DrValue, CrValue,Distance, OpeningKM, closingkm FROM tblloads WHERE DrValue IS NOT NULL) AS l ON f.VehicleNumber = l.VehicleNo AND l.loaddate >= '2020-06-01' and l.loaddate <= '2020-06-30'
LEFT JOIN (SELECT fleet , NULLIF(SUM(Liters),0) AS Liters,NULLIF(sum(distance),0) AS DDist, NULLIF(sum(distance)/ CASE WHEN sum(Liters)=0 THEN 1 ELSE sum(Liters) END,0) AS DDiesel, NULLIF(SUM (Manual_Amount),0) AS DieselCost FROM tblinput
WHERE [Date] >= '2020-06-01' And [Date] <= '2020-06-30'
GROUP BY Fleet) AS d ON l.VehicleNo = d.Fleet
LEFT JOIN (SELECT jd.fleet, SUM(Total) AS total From tblJobDetails jd, tbljobcards WHERE tbljobcards.JobID = jd.jobid AND jobdate >= '2020-06-01' AND jobdate <= '2020-06-30' GROUP BY jd.fleet) AS jobdetails ON jobdetails.fleet = l.vehicleno
WHERE VehicleCategory <> 'T'
GROUP BY f.vehiclenumber,f.fleettype,f.IsCreditor,d.Liters,d.ddist, d.DDiesel,d.DieselCost
ORDER BY fleettype
RESULTS

How to show 0 value using COUNT and SELECTon a SQL query

I have ONLY 1 table called Meeting that stores all meeting requests.
This table can be EMPTY.
It has several columns including requestType (which can only be "MT") meetingStatus (can only be either pending, approved, denied or canceled) and meetingCreatedTime
I want to count how many requests of each status's type (in other words how many requests are pending, how many are approved, denied and canceled) for the last 30 days
Problem is that if there is no request then nothing display but I want to display 0, how do I do it? Here is my query now:
SELECT [requestType],
( SELECT COUNT ([requestType]) FROM [Meeting] WHERE CAST([meetingCreatedTime] AS DATE) >= CAST(DateAdd(DAY,-30,Getdate()) AS DATE) AND [meetingStatus] = 'Approved') As 'Approved',
( SELECT COUNT ([requestType]) FROM [Meeting] WHERE CAST([meetingCreatedTime] AS DATE) >= CAST(DateAdd(DAY,-30,Getdate()) AS DATE) AND [meetingStatus] = 'Pending') As 'Pending',
( SELECT COUNT ([requestType]) FROM [Meeting] WHERE CAST([meetingCreatedTime] AS DATE) >= CAST(DateAdd(DAY,-30,Getdate()) AS DATE) AND [meetingStatus] = 'Canceled') As 'Canceled',
( SELECT COUNT ([requestType]) FROM [Meeting] WHERE CAST([meetingCreatedTime] AS DATE) >= CAST(DateAdd(DAY,-30,Getdate()) AS DATE) AND [meetingStatus] = 'Denied') As 'Denied'
FROM [Meeting]
WHERE CAST([meetingCreatedTime] AS DATE) >= CAST(DateAdd(DAY,-30,Getdate()) AS DATE) GROUP BY [requestType]
Result:
What I want is:
SELECT
RT.requestType,
SUM(CASE WHEN M.meetingStatus = 'Approved' THEN 1 ELSE 0 END) AS Approved,
SUM(CASE WHEN M.meetingStatus = 'Pending' THEN 1 ELSE 0 END) AS Pending,
SUM(CASE WHEN M.meetingStatus = 'Canceled' THEN 1 ELSE 0 END) AS Canceled,
SUM(CASE WHEN M.meetingStatus = 'Denied' THEN 1 ELSE 0 END) AS Denied,
FROM
(SELECT DISTINCT requestType FROM Meeting) RT
LEFT OUTER JOIN Meeting M ON
M.requestType = RT.requestType AND
M.meetingCreatedTime >= DATEADD(DAY, -30, GETDATE())
GROUP BY
RT.requestType
The SUMs are a much clearer (IMO) and much more efficient way of getting the counts that you need. Using the requestType table (assuming that you have one) lets you get results for every request type even if there are no meetings of that type in the date range. The LEFT OUTER JOIN to the meeting table allows the request type to still show up even if there are no meetings for that time period.
All of your CASTs between date values seem unnecessary.
Move those subqueries into simple sum/case statements:
select rt.request_type,
sum(case when [meetingStatus] = 'Approved' then 1 else 0 end),
sum(case when [meetingStatus] = 'Pending' then 1 else 0 end),
sum(case when [meetingStatus] = 'Canceled' then 1 else 0 end),
sum(case when [meetingStatus] = 'Denied' then 1 else 0 end)
from ( select 'MT' ) rt (request_type) --hopefully you have lookup table for this
left
join [Meeting] m on
rt.request_type = m.request_type and
CAST([meetingCreatedTime] AS DATE) >= CAST(DateAdd(DAY,-30,Getdate()) AS DATE)
group
by rt.request_type;
This is one possible approach to force one line to be visible in any case. Adapt this to your needs...
Copy it into an empty query window and execute... play around with the WHERE part...
DECLARE #Test TABLE (ID INT IDENTITY, GroupingKey VARCHAR(100));
INSERT INTO #Test VALUES ('a'),('a'),('b');
SELECT TOP 1 tbl.CountOfA
,tbl.CountOfB
,tbl.CountOfC
FROM
(
SELECT 1 AS Marker
,(SELECT COUNT(*) FROM #Test WHERE GroupingKey='a') AS CountOfA
,(SELECT COUNT(*) FROM #Test WHERE GroupingKey='b') AS CountOfB
,(SELECT COUNT(*) FROM #Test WHERE GroupingKey='c') AS CountOfC
WHERE (1=1) --play here with (1=0) and (1=1)
UNION ALL
SELECT 2,0,0,0
) AS tbl
ORDER BY Marker

Duplicates from an SQL Query

I have a dataset I retrieve from multiple joins. I have used SELECT DISTINCT in my statements but I still see duplicates in the result set. Here is the code:
SELECT DISTINCT Account
, PayoffAmtDOL as 'Payoff Amount DOL'
, PayoffAmtLOG as 'Payoff Amount LOG'
, PayoffAmountLive as 'Payoff Amount Live'
, [Difference]
, PrincipalBalance as 'Principal Balance'
, CreationDate as 'Date Entered System'
, CACSState as 'CACS State at Entry'
, PaymentsMade AS 'Payments Made'
, TotalPaymentAmount as 'Total Payment Amount'
, 'Liquidation Percentage' = CASE WHEN PayoffAmountLive = 0 THEN 1
WHEN ISNULL([Difference],0) = ISNULL(PayoffAmtDOL, 0) THEN 1
WHEN ISNULL([Difference],0) < 0 AND ISNULL(PayoffAmtDOL, 0) > 0 THEN 0
WHEN ISNULL([Difference],0) > 0 AND ISNULL(PayoffAmtDOL, 0) < 0 THEN 1
WHEN ISNULL([Difference],0) > ISNULL(PayoffAmtDOL, 0) THEN 1
WHEN [Difference] > 0 AND ISNULL(PayoffAmtDOL, 0) = 0 THEN 1
WHEN ISNULL(PayoffAmtDOL, 0) = 0 THEN 0
ELSE ISNULL([Difference],0)/ISNULL(PayoffAmtDOL, 0) END
, Cnt = 1
FROM
(
SELECT DISTINCT a.Account,
c.PayoffAmtDOL,
c.PayoffAmtLOG,
(ISNULL(c.PayoffAmtCACS, cacs.payoff_amt)) as 'PayoffAmountLive',
(ISNULL(c.PayoffAmtDOL, 0) - (ISNULL(c.PayoffAmtCACS , ISNULL(cacs.payoff_amt, 0)))) as 'Difference',
c.PrincipalBalance,
c.CreationDate,
c.CACSState,
(SELECT COUNT(PaymentID)
FROM tblATLPaymentInfo p
WHERE p.AccountID = a.AccountID
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) <= '3/27/2014'
) as 'PaymentsMade',
(SELECT SUM(PaymentAmount)
FROM tblATLPaymentInfo p
WHERE p.AccountID = a.AccountID
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) <= '3/27/2014'
) as 'TotalPaymentAmount'
FROM tblATLAcctInfo a
RIGHT JOIN tblATLClaimInfo c
ON c.AccountID = a.AccountID
LEFT JOIN SCFLOKYDCMSQL03.CACS_DM.dbo.Cacs_Info cacs
ON cacs.Account = a.Account
WHERE CONVERT(DATETIME, CONVERT(VARCHAR(10), c.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), c.CreationDate, 101)) <= '3/27/2014'
AND c.ClaimTypeID = (SELECT DISTINCT ClaimTypeID FROM tblATLClaimType WHERE ClaimType = 'N02 - Claims')
) a
ORDER BY Account
Here is an example of the duplicate rows:
AccountID DateEntered
123 01/19/2014
123 01/21/2014
345 02/1/2014
345 02/10/2014
The difference between appears to be the date entered. Maybe selecting the Row_Number() and then deleting the later date could be a solution
DISTINCT should not return multiple rows.. there should be at least one column that is different in each row, no? With character data, sometimes one can be fooled by non-visible differences, such as trailing spaces. Not sure if that is the case here, though.
Can you give an example of the duplicate rows?
OK, I see your edit. You have to select which of the dates to display. Try this to get the earliest date per AccountID:
SELECT AccountID, MIN(DateEntered) AS DateEntered
FROM ....
GROUP BY AccountID
ORDER BY AccountID
You can add more columns in the SELECT, as long as they are distinct you will not get more rows.
If you want, you can add COUNT(*) to the select to get the number of rows grouped.
DISTINCT will only reject lines that are exact duplicates, the DateEntered is different on each ID. If you want the latest, use Max(DateEntered)

counting events over flexible ranges

I am trying to count events (which are rows in the event_table) in the year before and the year after a particular target date for each person. For example, say I have a person 100 and target date is 10/01/2012. I would like to count events in 9/30/2011-9/30/2012 and in 10/02/2012-9/30/2013.
My query looks like:
select *
from (
select id, target_date
from subsample_table
) as i
left join (
select id, event_date, count(*) as N
, case when event_date between target_date-365 and target_date-1 then 0
when event_date between target_date+1 and target_date+365 then 1
else 2 end as after
from event_table
group by id, target_date, period
) as h
on i.id = h.id
and i.target_date = h.event_date
The output should look something like:
id target_date after N
100 10/01/2012 0 1000
100 10/01/2012 1 0
It's possible that some people do not have any events in the before or after periods (or both), and it would be nice to have zeros in that case. I don't care about the events outside the 730 days.
Any suggestions would be greatly appreciated.
I think the following may approach what you are trying to accomplish.
select id
, target_date
, event_date
, count(*) as N
, SUM(case when event_date between target_date-365 and target_date-1
then 1
else 0
end) AS Prior_
, SUM(case when event_date between target_date+1 and target_date+365
then 1
else 0
end) as After_
from subsample_table i
left join
event_table h
on i.id = h.id
and i.target_date = h.event_date
group by id, target_date, period
This is a generic answer. I don't know what date functions teradata has, so I will use sql server syntax.
select id, target_date, sum(before) before, sum(after) after, sum(righton) righton
from yourtable t
join (
select id, target_date td
, case when yourdate >= dateadd(year, -1, target_date)
and yourdate < target_date then 1 else 0 end before
, case when yourdate <= dateadd(year, 1, target_date)
and yourdate > target_date then 1 else 0 end after
, case when yourdate = target_date then 1 else 0 end righton
from yourtable
where whatever
group by id, target_date) sq on t.id = sq.id and target_date = dt
where whatever
group by id, target_date
This answer assumes that an id can have more than one target date.

multi-select sql query with date range

I have this query where I get totals of different stats from an employee roster table.
SELECT A.rempid AS EmpId,
E.flname,
A.rdo_total,
B.grave_total,
C.sundays,
D.holidays
FROM (SELECT rempid,
Count(rshiftid)AS RDO_Total
FROM rtmp1
WHERE rshiftid = 2
GROUP BY rempid
HAVING Count(rshiftid) > 0) A,
(SELECT rempid,
Count(rshiftid)AS Grave_Total
FROM rtmp1
WHERE rshiftid = 6
GROUP BY rempid
HAVING Count(rshiftid) > 0)B,
(SELECT rempid,
Count(rshiftid) AS Sundays
FROM rtmp1
WHERE Datepart(dw, rdate) = 1
AND rshiftid > 2
GROUP BY rempid
HAVING Count(rshiftid) > 0)C,
(SELECT rempid,
Count(rshiftid) AS Holidays
FROM rtmp1
WHERE rdate IN (SELECT pubhdt
FROM pubhol)
AND rshiftid > 2
GROUP BY rempid
HAVING Count(rshiftid) > 0)D,
(SELECT empid,
[fname] + ' ' + [sname] AS flName
FROM remp1)E
WHERE A.rempid = B.rempid
AND A.rempid = E.empid
AND A.rempid = C.rempid
AND A.rempid = D.rempid
ORDER BY A.rempid
I would like to add a date range into it, so that I can query the database within 2 dates. The rTmp1 table has a column called rDate. I was wondering what the best way to do this. I could add it to a stored procedure and add variable to each select query. Or is there a better way to run the query within a date range.
i think just add an additional where clause item similar to:
AND ( rDate > somedate AND rDate < someotherdate )
Adding the date range to each query is the most direct solution.
Making it a stored procedure is something that can always be done with a query, but has nothing to do with this specific case.
If the number of records resulting from narrowing down your table to the specified date range is substantially less than the entire table, it might be an option to insert these records into a temporary table or a table variable and run your existing query on that table/resultset.
Though I do not have any data to test, you might consider the following query as it is more easy to read and might perform better. But you have to check the results for yourself and maybe do some adjustments.
DECLARE #startDate date = '12/01/2012'
DECLARE #endDate date = DATEADD(MONTH, 1, #startDate)
SELECT
[e].[empid],
[e].[fname] + ' ' + [e].[sname] AS [flName],
SUM(CASE WHEN [t].[rshiftid] = 2 THEN 1 ELSE 0 END) AS [RDO_Total],
SUM(CASE WHEN [t].[rshiftid] = 6 THEN 1 ELSE 0 END) AS [Grave_Total],
SUM(CASE WHEN [t].[rshiftid] > 2 AND DATEPART(dw, [t].[rdate]) = 1 THEN 1 ELSE 0 END) AS [Sundays],
SUM(CASE WHEN [t].[rshiftid] > 2 AND [h].[pubhdt] IS NOT NULL THEN 1 ELSE 0 END) AS [Holidays]
FROM [remp1] [e]
INNER JOIN [rtmp1] [t] ON [e].[empid] = [t].[rempid]
LEFT JOIN [pubhol] [h] ON [t].[rdate] = [h].[pubhdt]
WHERE [t].[rdate] BETWEEN #startDate AND #endDate
GROUP BY
[e].[empid],
[e].[fname],
[e].[sname]
ORDER BY [empid] ASC