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;
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
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.