Rank or merge sequential rows - sql-server

I have a log file I need to either rank (but treating sequential and equal rows as ties), or merge sequential equal rows (based on specific column). My table looks like below, The Start and Stop are all being sequential (within the same ID window)
ID Start Stop Value
1 0 1 A
1 1 2 A
1 2 3 A
1 3 4 B
1 4 5 B
1 5 6 A
2 3 4 A
I have two approches to get what I need.
Approach 1: Rank (treating sequential rows with equal values in "Value" as ties) and using ID as partition.
This should give the output below. But how do I do the special rank: Treating sequential rows with equal values in "Value" as ties.
Select *,
rank() OVER (partition by id order by start, stop) as Rank,
XXX as SpecialRank
from Table
ID Start Stop Value Rank SpecialRank
1 0 1 A 1 1
1 1 2 A 2 1
1 2 3 A 3 1
1 3 4 B 4 2
1 4 5 B 5 2
1 5 6 A 6 3
2 3 4 A 1 1
Approach 2: Merge sequential rows with equal values in "Value".
This will shall create a table like below.
ID Start Stop Value
1 0 3 A
1 3 5 B
1 5 6 A
2 3 4 A
I don't know if this helps, but I have also a nextValue column that might help in this
ID Start Stop Value NextValue
1 0 1 A A
1 1 2 A A
1 2 3 A B
1 3 4 B B
1 4 5 B A
1 5 6 A A
2 3 4 A ...
Example-table:
CREATE TABLE #Table ( id int, start int, stop int, Value char(1), NextValue char(1));
INSERT INTO #Table values (1,0, 1, 'A', 'A');
INSERT INTO #Table values (1,1, 2, 'A', 'A');
INSERT INTO #Table values (1,2, 3, 'A', 'B');
INSERT INTO #Table values (1,3, 4, 'B', 'B');
INSERT INTO #Table values (1,4, 5, 'B', 'A');
INSERT INTO #Table values (1,5, 6, 'A', 'A');
INSERT INTO #Table values (2,3, 4, 'A', null);

Use a self join to an aggregate subquery from the full set, e.g.
with rankTable (id, value) as
( select 1, 'A' union all select 1, 'A' union all select 1, 'B' union all select 2, 'A')
select t2.* from rankTable t1 join (
select id, value, rank() over (partition by id order by value) as specialRank from
(
select distinct id, value
from rankTable
) t) t2 on t2.id =t1.id and t2.value = t1.value
id value specialRank
1 A 1
1 A 1
1 B 2
2 A 1

Related

Separate a group of values in to A or B (1 or 2) SQL

Note: I have already asked a similar question but had omitted a key part in the fact that a tool has many components.
I have a list of multiple tools and their components that all have a model number. I want to group every second tool based on the model it belongs to.
The derivedColumn is the query I want to return
declare #t table (Model int, toolID INT ,Component INT,DerivedColumn int);
insert into #t values (1,1,1,1),(1,1,2,1),(1,1,3,1),(1,2,1,2),(1,2,2,2),(1,2,3,2),(1,3,1,1),(1,3,2,1),(1,3,3,1),(1,4,1,2),(1,4,2,2),(1,4,3,2),(1,5,1,1),(1,5,2,1),(1,5,3,1),(2,1,1,1),(2,1,2,1),(2,2,1,2),(2,2,2,2),(2,3,1,1),(2,3,2,1)
SELECT * FROM #t
Model toolID Component DerivedColumn
1 1 1 1
1 1 2 1
1 1 3 1
1 2 1 2
1 2 2 2
1 2 3 2
1 3 1 1
1 3 2 1
1 3 3 1
1 4 1 2
1 4 2 2
1 4 3 2
1 5 1 1
1 5 2 1
1 5 3 1
2 1 1 1
2 1 2 1
2 2 1 2
2 2 2 2
2 3 1 1
2 3 2 1
Every second tool belonging to a model should have an alternative group number.
I believe I have to use a windows function but haven't been able to solve.
You could use dense_rank() and mod function %2 to calculate
DECLARE #SampleData AS TABLE
(
Model int,
ToolId int,
Component int
)
INSERT INTO #SampleData
(
Model, ToolId, Component
)
VALUES
(1, 1, 1),(1, 1, 2),(1, 1, 3),(1, 2, 1),
(1, 2, 2),(1, 2, 3),(1, 3, 1),(1, 3, 2),
(1, 3, 3),(1, 4, 1),(1, 4, 2),(1, 4, 3),
(1, 5, 1),(1, 5, 2),(1, 5, 3),(2, 1, 1),
(2, 1, 2),(2, 2, 1),(2, 2, 2),(2, 3, 1),
(2, 3, 2)
SELECT *,
CASE (dense_rank() OVER(PARTITION BY sd.Model ORDER BY sd.ToolId) + 1) % 2
WHEN 1 THEN 2
WHEN 0 THEN 1
END as DerivedColumn
FROM #SampleData sd
ORDER BY sd.Model, sd.ToolId
Demo link: http://rextester.com/LIQL79881
Hope it may helps you
DECLARE #T TABLE (Model INT, toolID INT ,Component INT,DerivedColumn INT);
INSERT INTO #T VALUES (1,1,1,1),(1,1,2,1),(1,1,3,1),(1,2,1,2),(1,2,2,2),(1,2,3,2),(1,3,1,1),(1,3,2,1),(1,3,3,1),(1,4,1,2),(1,4,2,2),(1,4,3,2),(1,5,1,1),(1,5,2,1),(1,5,3,1),(2,1,1,1),(2,1,2,1),(2,2,1,2),(2,2,2,2),(2,3,1,1),(2,3,2,1)
SELECT Model
,toolID
,ROW_NUMBER()Over(Partition by toolID order by Model) AS AlternativetoolID
,Component
,DerivedColumn
from #t;

Return inserted rows in specified order for Sql Server

I need to return the rows from a table-valued insert in the same order they were specified.
I can use OUTPUT INTO
DECLARE #generated1 TABLE ([Id] varbinary(8), [OwnerId] [int]);
INSERT INTO [Blog] ([OwnerId])
OUTPUT INSERTED.*
INTO #generated1
VALUES ('1'),
('2'),
('1'),
('2'),
('2'),
('3'),
('3'),
('3'),
('3');
SELECT * FROM #generated1;
This usually works and returns
Id OwnerId
===========================
0x418B6EC7C6AC864D 1
0x6D0B89E56AB3EC48 2
0xE1B86C6A3C64AB42 1
0x51B8D9D1FCDE1647 2
0xB5AD578020CBCE4C 2
0x56CD3FF610080841 3
0x1D0D5B370A732C43 3
0x0B71CDB5CE6E0445 3
0x6A8AE3A2BD19924E 3
But if there is an FK defined on OwnerId and more than 125 rows are inserted the order in which they are inserted is different from the specified order.
One way this could be accomplished is by adding a sequential value to each row to be inserted, joining the generated table with the values that were specified and ordering by the added sequential value:
DECLARE #inserted1 TABLE ([Order] [int], [OwnerId] [int]);
INSERT INTO #inserted1
VALUES ('1', '1'),
('2', '2'),
('3', '1'),
('4', '3'),
('5', '2'),
('6', '3'),
('7', '3'),
('8', '2'),
('9', '3');
DECLARE #generated1 TABLE ([Id] varbinary(8), [OwnerId] [int]);
INSERT INTO [Blog] ([OwnerId])
OUTPUT INSERTED.[Id], INSERTED.[OwnerId]
INTO #generated1
SELECT [OwnerId] FROM #inserted1;
SELECT *
FROM (SELECT [g].[Id], [g].[OwnerId], [i].[Order]
FROM #generated1 [g]
INNER JOIN #inserted1 [i]
ON [g].[OwnerId] = [i].[OwnerId]) t
ORDER BY [Order];
But since OwnerId is non-unique this will produce more rows than inserted:
Id OwnerId Order
0x2557DCF354F9CD4E 1 1
0x3A265F70A2018249 1 1
0xA21503CD2F928144 2 2
0xE8C593480FCEAF41 2 2
0xC3E3C969BEA87641 2 2
0x2557DCF354F9CD4E 1 3
0x3A265F70A2018249 1 3
0x3F7EBD8EE702B44B 3 4
0xA3F09A3A612ACF41 3 4
0xA45D8F6FF779A74C 3 4
0x7BA9521290232D43 3 4
0xA21503CD2F928144 2 5
0xE8C593480FCEAF41 2 5
0xC3E3C969BEA87641 2 5
0x3F7EBD8EE702B44B 3 6
0xA3F09A3A612ACF41 3 6
0xA45D8F6FF779A74C 3 6
0x7BA9521290232D43 3 6
0x3F7EBD8EE702B44B 3 7
0xA3F09A3A612ACF41 3 7
0xA45D8F6FF779A74C 3 7
0x7BA9521290232D43 3 7
0xA21503CD2F928144 2 8
0xE8C593480FCEAF41 2 8
0xC3E3C969BEA87641 2 8
0x3F7EBD8EE702B44B 3 9
0xA3F09A3A612ACF41 3 9
0xA45D8F6FF779A74C 3 9
0x7BA9521290232D43 3 9
There are still only 9 unique values in Id and Order columns, the way they are combined shouldn't matter however, since the only value that identifies the row is OwnerId. The trick is to remove the rows from the result in a way that only 9 rows are returned with values in Id and Order being unique. Partitioning on both of the columns provides a way to deterministically order the combinations:
SELECT *
FROM (SELECT [g].[Id], [g].[OwnerId], [i].[Order],
ROW_NUMBER() OVER (PARTITION BY [g].[Id] ORDER BY [i].[Order]) AS RowNumber,
ROW_NUMBER() OVER (PARTITION BY [i].[Order] ORDER BY [g].[Id]) AS RowNumber2
FROM #generated1 [g]
INNER JOIN #inserted1 [i]
ON [g].[OwnerId] = [i].[OwnerId]) t
WHERE RowNumber = RowNumber2
ORDER BY [Order];
This returns the rows in the expected order:
Id OwnerId Order RowNumber RowNumber2
======================================================
0x2A51E4E35D2FA040 1 1 1 1
0x787E303904EC764C 2 2 1 1
0x778CE142E9760248 1 3 2 2
0xC056C57F1729E643 3 4 1 1
0xC0706FF6A8890E40 2 5 2 2
0x0E2058F3F142DF42 3 6 2 2
0x4690B24BE196374B 3 7 3 3
0x9F70CA6011ECD449 2 8 3 3
0xF35D87D1BDB2C34F 3 9 4 4

Check All Child Records Exists Or Not Of Parent Id In Oracle

Table A :
PRID PRTRNSID
1 1
1 2
1 3
2 1
2 2
In this table , For PRID 1 there are 3 child records and For PRID 2 there are 2 child records
Table B :
EVENTTRNID PRID PRTRNSID
1 1 1
2 1 2
3 2 1
4 2 2
In this table , all child records for prid 2 is exists while for prid 1,3rd number child record is missing ,so i need output in following way
OutPut :
PRID Status
1 Pending
2 Done
Can anyone help me for check all child ids exists in another table or not ?
Try this
with A as (select 1 PRID, 1 PRTRNSID from dual
union all
select 1, 2 from dual
union all
select 1, 3 from dual
union all
select 2, 1 from dual
union all
select 2, 2 from dual),
B as (select 1 EVENTTRNID, 1 PRID, 1 PRTRNSID from dual
union all
select 2, 1, 2 from dual
union all
select 3, 2, 1 from dual
union all
select 4, 2, 2 from dual)
select A.prid, case when min(case when B.EVENTTRNID is null then 0 else B.EVENTTRNID end) = 0 then 'Pending' else 'Done' end Status
from A left join B on B.PRID = A.PRID and B.PRTRNSID = A.PRTRNSID
group by A.prid

How can one use T-SQL to select only those rows that have column B Value not greater than the next greater values in column A?

A B
1 2
1 1
1 3
1 4
3 3
3 4
4 5
How can one use T-SQL to select only those rows that have column B value not greater than the next greater values in column A?
For example, if above table is the input, the output should be as follows;
A B
1 2
1 1
3 3
4 5
select t1.A, t1.B
from tbl t1
where t1.B < isnull((select min(t2.A)
from tbl t2
where t2.A > t1.A), t1.B+1)

Tsql group by clause with exceptions

I have a problem with a query.
This is the data (order by Timestamp):
Data
ID Value Timestamp
1 0 2001-1-1
2 0 2002-1-1
3 1 2003-1-1
4 1 2004-1-1
5 0 2005-1-1
6 2 2006-1-1
7 2 2007-1-1
8 2 2008-1-1
I need to extract distinct values and the first occurance of the date. The exception here is that I need to group them only if not interrupted with a new value in that timeframe.
So the data I need is:
ID Value Timestamp
1 0 2001-1-1
3 1 2003-1-1
5 0 2005-1-1
6 2 2006-1-1
I've made this work by a complicated query, but am sure there is an easier way to do it, just cant think of it. Could anyone help?
This is what I started with - probably could work with that. This is a query that should locate when a value is changed.
> SELECT * FROM Data d1 join Data d2 ON d1.Timestamp < d2.Timestamp and
> d1.Value <> d2.Value
It probably could be done with a good use of row_number clause but cant manage it.
Sample data:
declare #T table (ID int, Value int, Timestamp date)
insert into #T(ID, Value, Timestamp) values
(1, 0, '20010101'),
(2, 0, '20020101'),
(3, 1, '20030101'),
(4, 1, '20040101'),
(5, 0, '20050101'),
(6, 2, '20060101'),
(7, 2, '20070101'),
(8, 2, '20080101')
Query:
;With OrderedValues as (
select *,ROW_NUMBER() OVER (ORDER By TimeStamp) as rn --TODO - specific columns better than *
from #T
), Firsts as (
select
ov1.* --TODO - specific columns better than *
from
OrderedValues ov1
left join
OrderedValues ov2
on
ov1.Value = ov2.Value and
ov1.rn = ov2.rn + 1
where
ov2.ID is null
)
select * --TODO - specific columns better than *
from Firsts
I didn't rely on the ID values being sequential and without gaps. If that's the situation, you can omit OrderedValues (using the table and ID in place of OrderedValues and rn). The second query simply finds rows where there isn't an immediate preceding row with the same Value.
Result:
ID Value Timestamp rn
----------- ----------- ---------- --------------------
1 0 2001-01-01 1
3 1 2003-01-01 3
5 0 2005-01-01 5
6 2 2006-01-01 6
You can order by rn if you need the results in this specific order.

Resources