SQL Server 2012 - assistance with self join or CTE - sql

DBMS: SQL Server 2012
I have a table with the following columns
What I need to be able to do is figure out the NextCarDepartureTime. So for example, Car2 would depart at 9/30/2014 1:12 AM, Car3 would depart at 10/1/2014 12:10 AM. The ultimate goal is to figure out the difference in hours between CarDepartedDateTime and NextCarDepartedTime. So the end result would look like
Any help would be greatly appreciated! Thanks

You can use the LEAD analytic function introduced in SQL server 2012. This function access subsequent row from your table. So your query now look like this -
;WITH myCTE
AS ( SELECT T1.Year ,
T1.Month ,
T1.CarNumberID ,
T1.CarDepartedDateTime ,
T1.CarDepartedNumberID ,
LEAD(T1.CarDepartedDateTime) OVER ( ORDER BY T1.id ) AS [NextCarDepartureTime] ,
LEAD(T1.CarNumberID) OVER ( ORDER BY T1.id ) AS [NextCarNumberID]
FROM Test T1
)
SELECT * ,
DATEDIFF(HOUR, CarDepartedDateTime, NextCarDepartureTime) AS TurnAroundTime
FROM myCTE;
I have written one blog post for analytic function in my blog here : krishnrajrana.wordpress.com

This would be the basic syntax you would need:
SELECT
T1.Year, T1.Month, T1.CarNumberID, T1.CarDepartedDateTime, T1.NextCarNumberID,
T2.CarDepartedDateTime as [NextCarDepartureTime],
CONVERT(varchar,(T2.CarDepartedDateTime - T1.CarDepartedDateTime, 108) as [Turn Around Time]
FROM
DatabaseName..TableName T1
Left outer join DatabaseName..TableName T2 on T2.CarNumberID = T1.NextCarNumberID
Depending on other details in the table (Primary Keys, etc.) you may want to add details to the join filters and/or a WHERE clause.
There are various options available for how you want to format/display the [Turn Around Time] field using different values for the last parameter of the CONVERT function. See https://msdn.microsoft.com/en-us/library/ms187928.aspx for a list of different options.

Related

Add missing months with values from previous month

I need to use this SQL query for a software and get the time in a particular format hence the reason for the Time column however I need the query to insert the months that are missing with the value from the previous month. This is the query I currently have.
SELECT [accountnumber],SUM([postingamount]) AS Amount, [accountingdate],
convert(varchar(4),year(accountingdate))+'M'+ Format(DATEPART( MONTH, accountingdate) , '00')
AS [Time]
FROM [7 GL Detail MACL]
where [accountingdate]>='2019-01-01'
GROUP BY [accountingdate],[postingamount],[accountnumber]
Current Results
Expected Results
Since you didn't specify the RDBMS system you're using, I can't guarantee that this logic will work because every system uses slightly different SQL syntax.
However I used Rasgo datespine function to generate this SQL, as it is quite complex to wrap your head around, and tested it on Snowflake.
The main differences between Snowflake and other systems are: DATEADD and TABLE (GENERATOR())
In case you can't modify this to work in your system, here are the basic steps which you'll want to follow:
Select unique accountnumbers
Select unique dates (month beginnings?) This is where Snowflake uses GENERATOR but other systems might actually have a Calendar table you can select from
Cross Join (cartesian join) these to create every possible combination of accountnumber and date
Outer Join #3 to your data (might have to truncate your date to month-begin)
Filter out rows that dont apply. Like for instance you might have just inserted a row for 1/1/2019 for an account that didn't even begin until 12/12/2020.
WITH GLOBAL_SPINE AS (
SELECT
ROW_NUMBER() OVER (ORDER BY NULL) as INTERVAL_ID,
DATEADD('MONTH', (INTERVAL_ID - 1), '2019-01-01'::timestamp_ntz) as SPINE_START,
DATEADD('MONTH', INTERVAL_ID, '2022-06-01'::timestamp_ntz) as SPINE_END
FROM TABLE (GENERATOR(ROWCOUNT => 42))
),
GROUPS AS (
SELECT
accountnumber,
MIN(DESIRED_INTERVAL) AS LOCAL_START,
MAX(DESIRED_INTERVAL) AS LOCAL_END
FROM [7 GL Detail MACL]
GROUP BY
accountnumber
),
GROUP_SPINE AS (
SELECT
accountnumber,
SPINE_START AS GROUP_START,
SPINE_END AS GROUP_END
FROM GROUPS G
CROSS JOIN LATERAL (
SELECT
SPINE_START, SPINE_END
FROM GLOBAL_SPINE S
WHERE S.SPINE_START >= G.LOCAL_START
)
)
SELECT
G.accountnumber AS GROUP_BY_accountnumber,
GROUP_START,
GROUP_END,
T.*
FROM GROUP_SPINE G
LEFT JOIN {{ your_table }} T
ON DESIRED_INTERVAL >= G.GROUP_START
AND DESIRED_INTERVAL < G.GROUP_END
AND G.accountnumber = T.accountnumber;
You were also doing an aggregation step, but I figure once you get this complicated part down, you can figure out how to finally aggregate it the way you want it.

TSQL Repeat values on outer join

I've been working on this for some time now and I would like to get some help.
My database is SQL Server 2008 R2 ( I know, very old).
I basically have a transaction table that captures values per week, by job.
I would like to repeat the last value of a job until it finds the next value.
I have included some data from my table. The last column (values needed) is what I'm trying to achieve.
Thank you very much.
Bruce
image of data
I've tried the SQL below, but it is not giving the correct values. Please see the attachment.
SQL
select t.*, t2.percentcomp as value_needed
from #1 t
outer apply
(select top 1 t2.*
from #1 t2
where t2.job_skey = t.job_skey and
t2.COST_CODE_SKEY=t2.COST_CODE_SKEY and
t2.period_end_date <= t.period_end_date and
t2.percentcomp is not null
order by t.JOB_SKEY,t.phase,t.period_end_date desc
) t2
Attachment..view of SQL. Value_needed should begin with 5
You can do what you want using OUTER APPLY:
select t.*, t2.percent_comp as value_needed
from t outer apply
(select top 1 t2.*
from t t2
where t2.job_skey = t.job_skey and
t2.period_end_date_id <= t.period_end_date_id and
t2.percentcomp is not null
order by t2.period_start_date desc
) t2;
enter image description here

Skip rows for specific time in SQL

Need a help.
I have two timestamp columns, so basically I want to get the max and min value with a thirD column showing as timedifference. I am skipping any 12.am time so used the syntax below. ANy help how to achieve the third column, timedifference.. It is in DB2.
SELECT EMPID,MIN(STARTDATETIME),MAX(ENDDATETIME)
FROM TABLE
WHERE DATE(STARTDATETIME)= '2012-05-15' AND HOUR(STARTDATETIME)<>0 AND HOUR(ENDDATETIME)<>0
GROUP BY EMPID
You can use the results from that in an inner select, and use those values to define the TimeDifference column. My knowledge of DB2 is very limited, so I'm making some assumptions, but this should give you an idea. I'll update the answer if something is drastically incorrect.
Select EmpId,
MinStartDate,
MaxEndDate,
MaxEndDate - MinStartDate As TimeDifference
From
(
Select EMPID,
MIN(STARTDATETIME) As MinStartDate,
MAX(ENDDATETIME) As MaxEndDate
From Table
Where DATE(STARTDATETIME) = '2012-05-15'
And HOUR(STARTDATETIME) <> 0
And HOUR(ENDDATETIME) <> 0
Group By EMPID
) A

Trying to Calculate Month over Month percentage increase/decrease

I am trying to calculate Month over Month % change on data rows. For example my current output is:
DataDate |LocationId|Payment|MoM [Current placeholder column in script]
12-1-2013|LocationA |$5.00 |
1-1-2014 |LocationA |$10.00 |
2-1-2014 |LocationA |$100.00|
12-1-2013|LocationB |$50.00 |
1-1-2014 |LocationB |$25.00 |
2-1-2014 |LocationB |$50.00 |
I am pasting the results into Excel and then calculating the MoM by using the following formula:
((CurrentDataDate Payment/PreviousDataDate Payment)-1)]
I can not figure out where to even begin trying to accomplish this so I cant provide any coding from what i have tried...I have read about and attempted a correlated scalar query used to calculate running totals and tried to alter it to accomplish this...no dice...I tried with a Join and a subquery but i will admit my subquery abilities are less than adequate.
The code used to call this info is:
Declare #BeginDate as DateTime
Declare #EndDate as DateTime
Set #BeginDate = '12-01-2013'
Set #EndDate = '02-01-2014'
Select DataDate,LocationId,Payment,0 as MoM
From dbo.mytableview
Where DataMonth between #BeginDate and #EndDate
Desired output is:
DataDate |LocationId|Payment|MoM
12-1-2013|LocationA |$5.00 |
1-1-2014 |LocationA |$10.00 |1.0 [or 100%]
2-1-2014 |LocationA |$100.00|9.0 [or 900%]
12-1-2013|LocationB |$50.00 |
1-1-2014 |LocationB |$25.00 |-.50 [or -50%]
2-1-2014 |LocationB |$50.00 |1.0 [or 100%]
I am using Microsoft SQLServer 2008 R2. I also have/and can use the 2012 version if that is needed.
This works on SQL Server 2012:
with x as (
select datadate, locationid, payment,
lag(payment) over(partition by locationid order by datadate) as prev_payment
from table
)
select *, (payment/prev_payment)-1
from x
Here's another solution, works on earlier versions:
select *, (t2.payment/t1.payment)-1
from #t t1
left join #t t2 on datediff(month, t1.datadate, t2.datadate)=1
and t1.locationid = t2.locationid
However, this self-join solutions generally don't perform well with larger sets of data, and a cursor solution is preferred in such cases.
Although dean's solution is better, I just wanted to also post a solution for people that don't have SQL Server 2012 for completeness' sake (and since I had already started on it before dean posted his).
This can be accomplished using Common Table Expressions and the Row_Number() function:
WITH CTE AS
(
SELECT Row_Number() OVER (PARTITION BY locationid ORDER BY datadate) AS RN, datadate, locationid, payment
FROM table
)
SELECT
CTE2.*,
(CTE2.payment / CTE1.payment) - 1 AS MOM
FROM
CTE AS CTE1 RIGHT OUTER JOIN
CTE AS CTE2
ON
CTE1.RN = CTE2.RN-1
AND
CTE2.locationid = CTE1.locationid
ORDER BY
locationid

Calculate Absolute difference between rows in MsAccess

I have spent all morning on this and just can't get it right... I'd really appreciate the help of someone more knowledgable than myself to get this working.
I have a table with some data in that looks like this:
MonthYear WeekBeg. Week Value
Dec-10 27/12/2010 1 66.66
Jan-11 3/01/2011 2 50
Jan-11 10/01/2011 3 17.5
Jan-11 17/01/2011 4 20
Jan-11 24/01/2011 5 0
Jan-11 31/01/2011 6 50
Feb-11 7/02/2011 7 0
Feb-11 14/02/2011 8 74
Feb-11 21/02/2011 9 100
I'm sorry the table above doesn't look better... I need to calculate the difference between the values from week to week - so the results column in this case would be:
16.66
32.5
2.5
20
50
50
74
26
I've looked at lots of code on the net - (e.g. from this site) but can't seem to make it work. I added in the ABS function to make sure the differences were absolute values and got this working but the numbers themselves just aren't right.
I haven't posted what I ended up with as it just got into a bigger and bigger mess, but what I started with was the link above. Again, I'd be really grateful for any insight anyone is able to offer.
Many thanks
ADDED:
Thanks so much for the fast reply. Got this working easily - added a few bits:
SELECT T1.MonthYear AS [From], T2.MonthYear AS [To], T1.Week AS Week, T1.WeekBeg AS WeekBeg, ABS(T1.Value - T2.Value) AS Difference FROM Test AS T1 LEFT JOIN Test AS T2 ON T2.Week = T1.Week + 1
Only thing is the resulting difference values need to be in the second of the two rows whereas here they are in the first of the two. Is there any easy way of modifying this?
Many thanks again.
ADDED:
Would definitely be worth using the second option if possible as can't always guarantee weeks won't be missed out. I am probably missing something, but when I run the second option from Thomas, I get the message:
'The specified field [T1].[Datavalue] could refer to more than one table listed in the FROM clause of your SQL statement'.
I thought this might be to do with the field in the table being VALUE not DataValue, but when I change it, I get 'Type Mismatch in Expression' instead.
Many thanks.
Presuming the Week column is perfectly sequential:
Select T1.MonthYear As T1Year
, T1.WeekBeg As T1WeekBeg
, T2.MonthYear As T2Year
, T2.WeekBeg As T2WeekBeg
, [T2].[Value]-[T1].[Value] AS Expr1
From TableWithData AS T1
Left Join TableWithData AS T2
On T1.Week = T2.Week + 1;
It should be noted that this will not compile in the QBE designer. You will have to view and modify it purely through the SQL View (or in code)
If for some reason you could not depend on the Week number being sequential, then it gets trickier as you need to use a derived table. Again, this solution will only work in SQL View or in code:
Select T1.MonthYear, T1.WeekBeg
, T2.MonthYear, T2.WeekBeg
, [T2].[Value]-[T1].[Value] AS Diff
From (TableWithData AS T1
Inner Join (
Select T1.WeekBeg As T1WeekBeg
, Min(T2.WeekBg) As T2WeekBeg
From TableWithData As T1
Left Join TableWithData AS T2
On T2.WeekBeg > T1.WeekBeg
Group By T1.WeekBeg
) As Query1
On T1.WeekBeg = Query1.T1WeekBeg)
Inner Join TableWithData AS T2
On Query1.T2WeekBeg = T2.WeekBeg;
A version based off of the sample query from your base link. (It uses ORDERY BY on the Week field and TOP 1 too isolate a scalar value.)
SELECT t1.Value - (SELECT TOP 1 t2.Value FROM myTable AS t2
WHERE t2.Week < t1.Week
ORDER BY t2.Week DESC) AS t2Val
FROM myTable t1
WHERE (SELECT TOP 1 t3.Value FROM myTable AS t3
WHERE t1.Week < t3.Week) Is Not Null
ORDER BY t1.Week;
Should be close to working but the aliasing is very error prone. I suggest that if the week numbers are indded sequential that you go with Thomas' answer.