Sql Table Subgrouping upon Values - sql-server

I've vehicle tracking data, I want to create Vehicle stoppage report. Help me to write sql query for that. Actual Data and resultant data are like Table1 and Table2 respectively.
Thank you!

Ok, here is some working statement
declare #t TABLE(id INT, dt DATETIME, sp int)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:05', 12)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:06', 0)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:07', 0)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:08', 0)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:09', 5)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:10', 8)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:11', 0)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:12', 0)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:13', 0)
INSERT INTO #t VALUES(1, '2015-01-17 12:00:14', 7)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:05', 10)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:06', 0)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:07', 0)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:08', 0)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:09', 12)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:10', 0)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:11', 0)
INSERT INTO #t VALUES(2, '2015-01-17 12:00:12', 10)
;
WITH cte1
AS ( SELECT a.id ,
a.dt AS stdt ,
b.dt AS endt ,
a.sp ,
a.rn
FROM ( SELECT * ,
1
+ ROW_NUMBER() OVER ( PARTITION BY id ORDER BY dt ) AS n ,
ROW_NUMBER() OVER ( ORDER BY GETDATE() ) AS rn
FROM #t t1
) a
LEFT JOIN ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY id ORDER BY dt ) AS n
FROM #t t1
) b ON b.id = a.id
AND b.n = a.n
),
cte2
AS ( SELECT t1.id ,
t1.stdt ,
ISNULL(t1.endt, GETDATE()) AS endt ,
t1.sp ,
t1.rn ,
SUM(t2.sp) AS sum
FROM cte1 t1
INNER JOIN cte1 t2 ON t1.rn >= t2.rn
GROUP BY t1.id ,
t1.stdt ,
t1.endt ,
t1.sp ,
t1.rn
)
SELECT id ,
MIN(stdt) ,
MAX(stdt) ,
DATEDIFF(ss, MIN(stdt), MAX(stdt))
FROM cte2
WHERE sp = 0
GROUP BY id ,
SUM
Here is result
1 2015-01-17 12:00:06.000 2015-01-17 12:00:08.000 2
1 2015-01-17 12:00:11.000 2015-01-17 12:00:13.000 2
2 2015-01-17 12:00:06.000 2015-01-17 12:00:08.000 2
2 2015-01-17 12:00:10.000 2015-01-17 12:00:11.000 1
You have result for vehicle 1 12:06 to 12:08. But i really think that results should be 12:06 to 12:09. If so, just change
SELECT id ,
MIN(stdt) ,
MAX(stdt) ,
DATEDIFF(ss, MIN(stdt), MAX(stdt))
FROM cte2
WHERE sp = 0
GROUP BY id ,
SUM
to
SELECT id ,
MIN(stdt) ,
MAX(endt) ,
DATEDIFF(ss, MIN(stdt), MAX(endt))
FROM cte2
WHERE sp = 0
GROUP BY id ,
SUM

Related

Recursive CTE with hierarchy data

Sample data:
declare #docs table (docid int, name varchar(10), isfolder int)
declare #hierarchy table (childid int, parent varchar(10))
insert #docs values (1, 'Doc1', 0)
insert #docs values (2, 'Doc2', 0 )
insert #docs values (3, 'Folder1', 1 )
insert #docs values (4, 'Folder2', 1 )
insert #docs values (5, 'SubFolderA', 1 )
insert #docs values (6, 'SubFolderB', 1 )
insert #hierarchy values (1, 5)
insert #hierarchy values (1, 6)
insert #hierarchy values (2, 6)
insert #hierarchy values (5, 3)
insert #hierarchy values (6, 4)
I want to list the data above so I end up with the documents (isfolder = 0) and the folder paths that they are in.
Expected output:
DocId Name Path
--------------------------------------
1 Doc1 Folder1\SubFolderA
1 Doc1 Folder2\SubFolderB
2 Doc2 Folder2\SubFolderB
CTE
I started to write this next CTE as it said this can be used to create my output but I am wrong somewhere
;WITH folderCTE (docid, name) AS
(
-- Anchor member
SELECT docid, CAST(name AS varchar(max)) AS name
FROM #docs
WHERE isfolder = 0
UNION ALL
-- Recursive member that references expression_name.
SELECT d.docid, CAST(folderCTE.Name + '\' + d.name AS varchar(max)) AS name
FROM folderCTE
INNER JOIN #docs d ON d.docid = folderCTE.docid
)
-- references expression name
SELECT *
FROM folderCTE
Can anyone show me how I should be doing a CTE for this?
If you use a CTE which just expands the hierarchy for folders, and a second one to build the hierarchy, you can simply join to this to get the folder for your docs:
declare #docs table (docid int, name varchar(10), isfolder int)
declare #hierarchy table (childid int, parentid varchar(10))
insert #docs values (1, 'Doc1', 0)
insert #docs values (2, 'Doc2', 0 )
insert #docs values (3, 'Folder1', 1 )
insert #docs values (4, 'Folder2', 1 )
insert #docs values (5, 'SubFolderA', 1 )
insert #docs values (6, 'SubFolderB', 1 )
insert #docs values (7, 'SuperFold1', 1 )
insert #hierarchy values (1, 5)
insert #hierarchy values (1, 6)
insert #hierarchy values (2, 6)
insert #hierarchy values (5, 3)
insert #hierarchy values (6, 4)
insert #hierarchy values (3, 7)
;WITH folderCTE
AS
(
select docid, cast(name as nvarchar(max)) as name, parentid
from #docs d
left join #hierarchy h on d.docid=h.childid
where d.isfolder = 1
),
folderHierarchyCTE
as
(
select docid, cast(name as nvarchar(max)) as name
from folderCTE where parentid is null
union all
select d.docid, cast(p.name + '/' + d.name as nvarchar(max))
from folderCTE d
inner join folderHierarchyCTE p on d.parentid = p.docid
)
SELECT d.docid, d.name, f.name
FROM #docs d
inner join #hierarchy h on h.childid=d.docid
inner join folderHierarchyCTE f on h.parentid=f.docid
where d.isfolder = 0

Repeat the column value until value change in same column value

My table structure:
declare #TestTable as table
(
id int,
somedate date,
somevalue int
)
insert into #TestTable values
(1, '2019-01-01', 1000),
(2, '2019-01-02', null ),
(3, '2019-01-03', null),
(4, '2019-01-04', null ),
(5, '2019-01-05', 800),
(6, '2019-01-06', null),
(7, '2019-01-07', null),
(8, '2019-01-08', null),
(9, '2019-01-09', null),
(10, '2019-01-10', 700)
Repeat the column value until any change in value of column (somevalue).. Is it possible with window functions?
Required output:
You can achieve that by using window function as well.
try the following:
select id, somevalue, newvalue=max(somevalue) over (partition by c)
from
(
select id, somevalue
,c=count(somevalue) over (order by id)
from #testtable
) t
order by id;
Please find the demo here.
Try this below logic-
DEMO HERE
SELECT A.*,
(
SELECT somevalue
FROM #TestTable
WHERE id = (
SELECT MAX(id)
FROM #TestTable
WHERE id <= A.id
AND somevalue IS NOT NULL
)
) new_column
FROM #TestTable A
ORDER BY A.id
You can achieve this using COALESCE (Transact-SQL)
.
SELECT
[id]
, somedate
, COALESCE(somevalue,
(SELECT TOP (1) somevalue
FROM #TestTable AS p2
WHERE
p2.somevalue IS NOT NULL
AND p2.[id] <= p.[id] ORDER BY p2.[id] DESC))
FROM #TestTable AS p;
Here is the live db<>fiddle demo.

Can't figure out how to use XML path

Hi I have tried all I can, but can't figure out how to do this.
I have this table "T1"
----RID----PID----
1 1
1 2
which i left join on RID with another "T2" to get the name column of PID:
----RID-----PID-----Pname----
1 1 Task1
1 2 Task2
So far so good. But what I really want is at table with these columns: 1. (distinct RID) 2.(A count of PID's in each Rid) 3.( Comma-seperated string of all PID-names)
----RID-----PID-----Pname----
1 2 Task1,Task2
But if a RID only has one Pname then here should be no comma:
----RID-----PID-----Pname----
1 2 Task1,Task2
2 1 Task1
Anyone ? I have tried with XML PATH but can't figure it out.....
Full working example:
DECLARE #T1 TABLE
(
[RID] INT
,[PID] INT
);
INSERT INTO #T1 ([RID], [PID])
VALUES (1, 1)
,(1, 2);
DECLARE #T2 TABLE
(
[RID] INT
,[PID] INT
,[Pname] VARCHAR(12)
);
INSERT INTO #T2 ([RID], [PID], [Pname])
VALUES (1, 1, 'Task1')
,(1, 2, 'Task2');
SELECT T1.[RID]
,COUNT(T1.[PID]) AS [PID]
,[value] AS [PName]
FROM #T1 T1
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + T2.[Pname]
FROM #T2 T2
WHERE T2.[RID] = T2.[RID]
ORDER BY T2.[PID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) DS ([value])
GROUP BY T1. [RID]
,[value];

How do I find records out of order - SQL?

Let's say I have a table with an ID Identity column, some data, and a datestamp. Like this:
1 data 5/1/2013 12:30
2 data 5/2/2013 15:32
3 data 5/2/2013 16:45
4 data 5/3/2013 9:32
5 data 5/5/2013 8:21
6 data 5/4/2013 9:36
7 data 5/6/2013 11:42
How do I write a query that will show me the one record that is timestamped 5/4? The table has millions of records. I've done some searching, but I don't know what to call what I'm searching for. :/
declare #t table(id int, bla char(4), timestamp datetime)
insert #t values
(1,'data','5/1/2013 12:30'),
(2,'data','5/2/2013 15:32'),
(3,'data','5/2/2013 16:45'),
(4,'data','5/3/2013 9:32'),
(5,'data','5/5/2013 8:21'),
(6,'data','5/4/2013 9:36'),
(7,'data','5/6/2013 11:42')
select timestamp
from
(
select rn1 = row_number() over (order by id),
rn2 = row_number() over (order by timestamp), timestamp
from #t
) a
where rn1 not in (rn2, rn2-1)
in 2008 r2, this would be a way
DECLARE #Table AS TABLE
(id INT , ladate DATETIME)
INSERT INTO #Table VALUES (1, '2013-05-01')
INSERT INTO #Table VALUES (2, '2013-05-02')
INSERT INTO #Table VALUES (3, '2013-05-03')
INSERT INTO #Table VALUES (4, '2013-05-05')
INSERT INTO #Table VALUES (5, '2013-05-04')
INSERT INTO #Table VALUES (6, '2013-05-06')
INSERT INTO #Table VALUES (7, '2013-05-07')
INSERT INTO #Table VALUES (8, '2013-05-08')
--I added the records in the sort order but if not just make sure you are sorted in the query
SELECT t2.ladate FROM #Table T1
INNER JOIN #Table T2 ON T1.Id = T2.Id + 1
INNER JOIN #Table t3 ON t2.id = t3.id + 1
WHERE t3.ladate < t2.ladate AND t2.ladate > t1.ladate
-- I made the assumption that your Id are all there, 1,2,3,4,5.... none missing... if there are rownumbers missing, you can use row_number()

Comparing two select statements

I need to write a stored procedure in T-SQL that would do the following:
Get a list of items related to a certain SectionID
Select and return all of the SectionIDs that have the same list of items or more (not less)
The table structure is as follows:
Section ID | Item Name
1 Item1
2 Item1
1 Item2
1 Item3
2 Item2
So if I pass 1 as an ID, this should not return anything as SectionID 2 only has 2 of the 3 items that SectionID = 1 has, but if I pass SectionID = 2 as the parameter, this should return SectionID = 1.
Hopefully, I explained that properly. What would be a good approach for this?
Here is a full example of what you need:
create table #test (
SectionID int,
ItemName varchar(10)
)
insert into #test values (1, 'Item1')
insert into #test values (2, 'Item1')
insert into #test values (1, 'Item2')
insert into #test values (1, 'Item3')
insert into #test values (2, 'Item2')
insert into #test values (3, 'Item1')
insert into #test values (3, 'Item2')
insert into #test values (3, 'Item3')
declare #test int
select #test = 3
declare #dist int
select #dist = count(distinct ItemName) from #test where SectionID = #test
select distinct t0.SectionID from #test t0
left join (select distinct SectionID, ItemName from #test where SectionID = #test) t1
on t0.ItemName = t1.ItemName and t0.SectionID != t1.SectionID
where t0.SectionID != #test
group by t0.SectionID
having count(distinct t1.ItemName) >= #dist
drop table #test
In your case you just need this part:
declare #test int
select #test = 3 --input argument from stored procedure
declare #dist int
select #dist = count(distinct ItemName) from tablename where SectionID = #test
select distinct t0.SectionID from tablename t0
left join (select distinct SectionID, ItemName from tablename where SectionID = #test) t1
on t0.ItemName = t1.ItemName and t0.SectionID != t1.SectionID
where t0.SectionID != #test
group by t0.SectionID
having count(distinct t1.ItemName) >= #dist
Assuming the following Table...
DECLARE #Sections AS TABLE (Id INT, Item VARCHAR(25))
INSERT INTO #Sections
(Id, Item)
SELECT 1, 'Item1'
UNION SELECT 2, 'Item1'
UNION SELECT 1, 'Item2'
UNION SELECT 1, 'Item3'
UNION SELECT 2, 'Item2'
You can do this...
DECLARE #SectionId INT, #ItemCount INT
SELECT #SectionId = 2 --You'd change this to whatever
, #ItemCount = 0
SELECT #ItemCount = COUNT(*)
FROM #Sections
WHERE Id = #SectionId
SELECT s.Id
FROM #Sections AS p
JOIN #Sections AS s
ON s.Id != p.Id
AND s.Item = p.Item
WHERE p.Id = #SectionId
GROUP BY s.Id
HAVING COUNT(*) >= #ItemCount

Resources