I'm struggling with an algorithm in SQL server , and I have no clue how to solve this .
I have a column with timestamps on which I've ordered by desc .
I need to find out the differences between de 2nd row and the 1st , the 3rd row and the 2 nd as so on and display this in a new row .The final table should look like this :
ID Type Time_Stamp Difference
xxx YYY 00:03:12 00:00:02
xxx ZZZ 00:03:14 00:00:02
xxx ZZZ 00:03:16
Can I use some kind of SQL function ? Please let me know if you have any ideas .
Cheers ,
Use Window Function
;WITH cte
AS (SELECT Row_number()
OVER(
ORDER BY time_stamp) rn,
*
FROM yourtable)
SELECT a.ID,
a.Type,
a.Time_Stamp,
CONVERT(VARCHAR(10), Datediff(second, a.Time_Stamp, b.Time_Stamp)/3600)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), (Datediff(second, a.Time_Stamp, b.Time_Stamp)%3600)/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), Datediff(second, a.Time_Stamp, b.Time_Stamp)%60), 2) AS [Difference]
FROM cte a
LEFT JOIN cte b
ON a.rn = b.rn - 1
If you are using Sql Server 2012+ then use Lead Function
;WITH cte
AS (SELECT *,
Datediff(second, time_stamp, Lead(time_stamp)
OVER(
ORDER BY time_stamp)) AS Sec
FROM yourtable)
SELECT a.ID,
a.Type,
a.Time_Stamp,
CONVERT(VARCHAR(10), sec/3600) + ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), (sec%3600)/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), sec%60), 2) AS [Difference]
FROM cte a
Update : To insert into temp table do this.
;WITH cte
AS (SELECT Row_number()
OVER(
ORDER BY time_stamp) rn,
*
FROM yourtable)
SELECT a.ID,
a.Type,
a.Time_Stamp,
CONVERT(VARCHAR(10), Datediff(second, a.Time_Stamp, b.Time_Stamp)/3600)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), (Datediff(second, a.Time_Stamp, b.Time_Stamp)%3600)/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), Datediff(second, a.Time_Stamp, b.Time_Stamp)%60), 2) AS [Difference]
into #tempTable --Here you need to use into temptable
FROM cte a
LEFT JOIN cte b
ON a.rn = b.rn - 1
or create the temp table and use
......
Insert into #temptable
SELECT a.ID,
a.Type,
a.Time_Stamp
......
SAMPLE TABLE
CREATE TABLE #TEMP(ID VARCHAR(10),[TYPE] VARCHAR(10),TIME_STAMP TIME)
INSERT INTO #TEMP
SELECT 'xxx' , 'YYY', '00:03:12'
UNION ALL
SELECT 'xxx', 'ZZZ', '00:03:14'
UNION ALL
SELECT 'xxx', 'ZZZ', '00:03:16'
You need to do self join logic to get next row's record
QUERY
;WITH CTE AS
(
SELECT ROW_NUMBER()OVER(ORDER BY TIME_STAMP)RNO,*
FROM #TEMP
)
SELECT C1.*,'00:00:'+CAST(DATEDIFF(S,C1.TIME_STAMP,C2.TIME_STAMP)AS VARCHAR(2)) D
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
Click here to view result
Related
I have a data that looks as below. I would like to count total number of products and create a table that summarize the count for different date range. Please see diagram below as an example. Here the quay I have:
SELECT(
SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/01/2017 AND
'01/15/2017,
SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/16/2017 AND
'01/31/2017,
);
But I get an error:
Incorrect syntax near ','
This will work
SELECT
(SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/01/2017' AND
'01/15/2017'),
(SELECT Count(DISTINCT Product) FROM Table1 WHERE Date BETWEEN '01/16/2017' AND
'01/31/2017')
;
You have a few syntax errors in your SQL:
when using the BETWEEN statement, you must only put quotes around
each date. Don't include the AND in the quotes.
You need to put brackets around each of the inner SELECTS.
There is an extra comma at the end (before the closing bracket)
However, this query will not return values of 5 and 2, because you are specifying DISTINCT in each SELECT. That will give you only 3 and 2 because there are only 3 distinct values for Product (A/D/E) returned from the first query. Remove the distinct if you want the number of rows.
Finally, I recommend that you use the YYYY-MM-DD syntax when using date literals in your SQL. This removes any ambiguity about what is the date and the month. For example, 1/4/2017 could be 4 Jan or 1 April, depending on how your SQL Server was configured. If you specify 2017-04-01, then this will always be interpreted as 1 April, and 2017-01-04 will always be 4 Jan.
Simply :
SELECT
(SELECT COUNT(DISTINCT Product) FROM yourtable T1 WHERE Date BETWEEN '01/01/2017' AND '01/15/2017') C1,
(SELECT COUNT(DISTINCT Product) FROM yourtable T2 WHERE Date BETWEEN '01/16/2017' AND '01/31/201') C2
Outputs: 3 | 2
SELECT
(SELECT COUNT(*) FROM yourtable T1 WHERE Date BETWEEN '01/01/2017' AND '01/15/2017') C1,
(SELECT COUNT(*) FROM yourtable T2 WHERE Date BETWEEN '01/16/2017' AND '01/31/201') C2
Outputs : 5 | 2
If the last code doesn't work you need to use Convert and Cast:
Declare #tb table(product varchar(50),[Date] date)
insert into #tb
select 'A' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),4,2) as date) as [Date] union all
select 'A' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/1/2017' as date),110),4,2)as date) as [Date] union all
select 'D' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/5/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/5/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/5/2017' as date),110),4,2) as date) as [Date] union all
select 'E' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/6/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/6/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/6/2017' as date),110),4,2)as date) as [Date] union all
select 'E' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/10/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/10/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/10/2017' as date),110),4,2)as date) as [Date] union all
select 'D' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/25/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/25/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/25/2017' as date),110),4,2)as date) as [Date] union all
select 'A' as Product, cast(substring(CONVERT(VARCHAR(10), cast('1/30/2017' as date),110),7,4) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/30/2017' as date),110),1,2) + '-' +
substring(CONVERT(VARCHAR(10), cast('1/30/2017' as date),110),4,2)as date) as [Date]
--Copy from here
select acnt as 'Count(01/01/2017-01/15/2017)',bcnt as 'Count(01/16/2017-01/31/2017)' from
(select count(1) as acnt from #tb where [Date] BETWEEN '01/01/2017' AND '01/15/2017') as a
left join
(select count(1) as bcnt from #tb where [Date] BETWEEN '01/16/2017' AND '01/31/2017') as b
On a.acnt != b.bcnt or a.acnt = b.bcnt
;WITH CTE(Product ,[Date])
As
(
SELECT 'A','1/1/2017' Union all
SELECT 'A','1/1/2017' Union all
SELECT 'D','1/5/2017' Union all
SELECT 'E','1/6/2017' Union all
SELECT 'E','1/10/2017' Union all
SELECT 'D','1/25/2017' Union all
SELECT 'A','1/30/2017'
)
SELECT SUM([Count Of between '1/1/2017' AND '1/15/2017']) AS [Count Of between (1/1/2017 AND 1/15/2017)]
,SUM([Count Of between '1/16/2017' AND '1/31/2017']) AS [Count Of between (1/16/2017 AND 1/31/2017)]
FROM (
SELECT COUNT(Product) OVER (
PARTITION BY [Date] ORDER BY Product
) AS [Count Of between '1/1/2017' AND '1/15/2017']
,ISNULL(NULL, '') AS [Count Of between '1/16/2017' AND '1/31/2017']
FROM cte
WHERE [Date] BETWEEN '1/1/2017'
AND '1/15/2017'
UNION ALL
SELECT ISNULL(NULL, '')
,COUNT(Product) OVER (
PARTITION BY [Date] ORDER BY Product
) AS [Count Of between '1/16/2017' AND '1/31/2017']
FROM cte
WHERE [Date] BETWEEN '1/16/2017'
AND '1/31/2017'
) Dt
OutPut
Count Of between (1/1/2017 AND 1/15/2017) | Count Of between (1/16/2017 AND 1/31/2017)
--------------------------------------------------------------------------------
5 2
I have the following query to optimize.
Following used to get the details from the profit table.
First inner SELECT: in the first select statement I have to get the details from the profit table and assign the row number for each row.
Second inner SELECT: in the second select statement I have to do some calculation (sum).
Outer SELECT: And get the result by combining them on id and also do some manipulation with data.
Code:
SELECT
a.p_id, p_Name,
convert(varchar, a.EndDate, 107) EndDate,
convert(varchar, a.EndDate, 106) NewEndDate,
LTRIM(a.p_id)) + '' + REPLACE(LEFT(CONVERT(VARCHAR, a.EndDate, 106), 6) + '' + RIGHT(CONVERT(VARCHAR, a.EndDate, 106), 2), ' ', '') as Compo,
a.GP,
b.fpro FirstProfit,
(b.fpro - b.spro) prodiff,
a.Qtity * a.GP as Ov,
a.Qtity,
b.fproChanPer
FROM
(SELECT
p_Name, p_id,
EndDate,
GP, FirstProfit,
prodiff,
Qtity,
ROW_NUMBER() OVER (PARTITION By p_Name, p_id ORDER BY EndDate) Rown
FROM
tbl_profit) a,
(SELECT
p_id,
CAST(SUM(FirstProfit) AS DECIMAL(24,2)) fpro,
CAST(SUM(SecondProfit) AS DECIMAL(24,2)) spro,
CAST(CAST(SUM(prodiff) AS DECIMAL(24,2)) / CAST(SUM(SecondProfit) AS DECIMAL(24,2)) * 100 AS DECIMAL(24,2)) fproChanPer
FROM
tbl_profit
GROUP BY
p_id) b
WHERE
b.p_id = a.p_id
AND Rown = 1
My question: can I combine those two (alias a and b) inner SELECT statements for query optimization?
My attempt: I have tried by using the following query but getting different calculation result.
SELECT p_Name,
p_id,
EndDate,
GP,
FirstProfit,
prodiff,
Qtity
ROW_NUMBER()OVER (PARTITION By p_Name,p_id ORDER By EndDate ) Rown,
CAST(SUM(FirstProfit) AS DECIMAL(24,2)) fpro,
CAST(SUM(SecondProfit) AS DECIMAL(24,2)) spro,
CAST(CAST(SUM(prodiff) AS DECIMAL(24,2)) /CAST(SUM(SecondProfit) AS DECIMAL(24,2)) * 100 AS DECIMAL(24,2)) fproChanPer
FROM tbl_profit
GROUP By p_id,p_Name,EndDate,GP,FirstProfit,prodiff,Qtity
tbl_profit must have multiple P_Name for a single p_id , when you are grouping by p_id only you are getting correct aggregate value whereas because of multiple p_name for a single p_id when you are grouping by p_id and p_name your aggregate sum values are getting double for wherever single p_id has more than one p_name.
The way you written query to get Rown = 1 records in this case is perfect, thought still have scope to optimize I have done few things please check.
SELECT
a.p_id,
p_Name,
CONVERT(varchar, a.EndDate, 107) EndDate,
CONVERT(varchar, a.EndDate, 106) NewEndDate,
LTRIM(a.p_id) + '' + REPLACE(LEFT(CONVERT(varchar, a.EndDate, 106), 6) + '' + RIGHT(CONVERT(varchar, a.EndDate, 106), 2), ' ', '') AS Compo,
a.GP,
b.fpro AS FirstProfit,
(b.fpro - b.spro) prodiff,
a.Qtity * a.GP AS Ov,
a.Qtity,
b.fproChanPer
FROM (
SELECT
a.p_Name,
a.p_id,
a.EndDate,
a.GP,
a.FirstProfit,
a.prodiff,
a.Qtity,
ROW_NUMBER() OVER (PARTITION BY a.p_Name, a.p_id ORDER BY a.EndDate) Rown
FROM tbl_profit AS a WITH (NOLOCK)
) AS a
INNER JOIN
(
SELECT
b.p_id,
CAST(SUM(b.FirstProfit) AS decimal(24, 2)) fpro,
CAST(SUM(b.SecondProfit) AS decimal(24, 2)) spro,
CAST(CAST(SUM(b.prodiff) AS decimal(24, 2)) / CAST(SUM(b.SecondProfit) AS decimal(24, 2)) * 100 AS decimal(24, 2)) fproChanPer
FROM tbl_profit AS b WITH (NOLOCK)
GROUP BY b.p_id
) AS b
ON b.p_id = a.p_id
AND a.Rown = 1
How do I show the missing days in the query below which uses a join on a calendar table in SQL Server 2012?
The values for the missing day should be 0.
;WITH g AS
(
SELECT calendar.year, calendar.month, calendar.day, calendar.MonthName,
CAST(calendar.day AS NVARCHAR(2)) + '.' + CAST(calendar.month AS NVARCHAR(2)) + '.' + CAST(calendar.year AS NVARCHAR(16))AS DayMonthAndYear,
calendar.MonthName + ' ' + CAST(calendar.year AS NVARCHAR(16)) AS MonthNameAndYear,
COUNT(profileviews.ID) AS total_profileviews
FROM core_Calendar AS calendar
LEFT JOIN members_ProfileViews AS profileviews
ON CONVERT(DATE, calendar.date) = CONVERT(DATE, profileviews.CreatedAt)
WHERE calendar.date >= CONVERT(DATE, '03.02.2015')
AND calendar.date <= CONVERT(DATE, '03.02.2016')
AND profileviews.MemberID = 10
GROUP BY calendar.year, calendar.month, calendar.day, calendar.MonthName
)
SELECT g.year, g.month, g.day, g.MonthName, g.DayMonthAndYear, g.MonthNameAndYear, total_profileviews,
SUM(g.total_profileviews) OVER (ORDER BY g.year,g.month,g.day ROWS UNBOUNDED PRECEDING) AS rt_profileviews
FROM g
ORDER BY g.year, g.month, g.day;
You need to move the profileviews.MemberID = 10 to the LEFT JOIN, since it's essentially converting it to an INNER JOIN:
WITH g AS
(
SELECT C.[year],
C.[month],
C.[day],
C.[MonthName],
CONVERT(VARCHAR(10),C.[date],104) AS DayMonthAndYear,
C.[MonthName] + ' ' + CAST(C.[year] AS NVARCHAR(16)) AS MonthNameAndYear,
COUNT(profileviews.ID) AS total_profileviews
FROM core_Calendar AS C
LEFT JOIN ( SELECT *
FROM members_ProfileViews
WHERE MemberID = 10) AS P
ON CONVERT(DATE, C.[date]) = CONVERT(DATE, P.CreatedAt)
WHERE C.[date] >= CONVERT(DATE, '03.02.2015',104)
AND C.[date] <= CONVERT(DATE, '03.02.2016',104)
GROUP BY C.[year], C.[month], C.[day], C.[MonthName]
)
SELECT g.[year],
g.[month],
g.[day],
g.[MonthName],
g.DayMonthAndYear,
g.MonthNameAndYear,
total_profileviews,
SUM(g.total_profileviews) OVER (ORDER BY g.[year],g.[month],g.[day] ROWS UNBOUNDED PRECEDING) AS rt_profileviews
FROM g
ORDER BY g.[year], g.[month], g.[day];
I'm trying to get the SUM of the quantity sold on a day. I need the results to be in ORDER BY date. The query below gives me the exact result I need, except the date is not formatted to what I need.
SELECT CAST(Datetime AS DATE) AS 'date', SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.datetime > '05/15/2015'
GROUP BY CAST(Datetime AS DATE)
SELECT DATENAME(MM, datetime) + ' ' + CAST(DAY(datetime) AS VARCHAR(2)) AS [DD Month], SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.datetime > '05/15/2015'
GROUP BY DATENAME(MM, datetime) + ' ' + CAST(DAY(datetime) AS VARCHAR(2))
Results for the top query:
2015-05-15 91.43
2015-05-16 84.77
Results for the bottom query:
June 1 128.34
June 10 85.06
The top query gives me the information I need in the order I need it by.
The bottom query gives me the date format I need but in the wrong order.
If you really need to apply different formatting, you can do it using a derived table:
select
DATENAME(MM, [date]) + ' ' + CAST(DAY([date]) AS VARCHAR(2)) AS [DD Month],
total_quantity
from
(
SELECT CAST([Datetime] AS DATE) AS [date], SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.[datetime] > '05/15/2015'
GROUP BY CAST(Datetime AS DATE)
) as X
This way you can wrap to results into the derived table, and then apply rest of the operations on that.
I would assume this would work too, but can't test now:
SELECT DATENAME(MM, CAST([datetime] AS DATE)) + ' '
+ CAST(DAY(CAST([datetime] AS DATE)) AS VARCHAR(2)) AS [DD Month],
SUM(quantity) as total_quantity
FROM Invoice_Itemized ii
INNER JOIN Invoice_Totals it ON it.Invoice_Number = ii.Invoice_Number
WHERE ii.ItemNum = '4011'
AND it.[datetime] > '05/15/2015'
GROUP BY CAST([datetime] AS DATE)
I have 2 tables which I am wanting to run a query on. The first table is dbo.Incidents which contacts the primary key IncidentID. The second table is dbo.IncidentActions which has the primary key ActionID and has a field IncidentID which links with the first table.
There are many actions with the same IncidentID and I am wanting to return 1 row only per IncidentID with the last ActionID for that IncidentID.
Thanks Andomar - nearly there I promise :)
select *
from (
select i.IncidentID
, ia.ActionID
, RIGHT('' + CAST(DATEDIFF(mi, ia.ActionTime, CONVERT([varchar], GETDATE(), 14))
/ 60 % 60 AS VARCHAR), 2) + ' hr(s) ' + RIGHT('' + CAST(DATEDIFF(mi, ia.ActionTime, CONVERT([varchar], GETDATE(), 14)) % 60 AS VARCHAR), 2) + ' min(s)' AS LastActionTime
, row_number() over (
partition by i.IncidentID
order by ia.ActionID desc) as rn
from dbo.Incident i
join dbo.IncidentAction ia
on i.IncidentID = ia.IncidentID
) as SubQueryAlias
where rn = 1
This is all working now I just want to set Where ia.ActionDate = GetDate() - can't seem to get that working
If you're just looking for the top ActionID per incident:
select i.IncidentID
, max(ia.ActionID) as MaxActionIdForIncident
from Incidents i
join IncidentActions ia
on i.IncidentID = ia.IncidentID
group by
i.IncidentID
If the IncidentActions table has a timestamp column you'd like to use to determine which row to return, you could use the row_number() window function:
select *
from (
select i.IncidentID
, ia.ActionID
, ia.ActionByUser -- Note: now you return any column
, row_number() over (
partition by i.IncidentID
order by ia.ActionTimestamp desc) as rn
from Incidents i
join IncidentActions ia
on i.IncidentID= ia.IncidentID
) as SubQueryAlias
where rn = 1 -- Latest action per incident only
The subquery is required because you can't use window functions in the where clause. For more examples, browse the greatest-n-per-group tag.