Update column with a dynamic sequence with Row_number() - sql

I tried to update in MSSQL a column(Y) of a table(A) with with an ascending sequence that resets itself when the value of another column(X) of the same table changes.
Table A at the beginning:
id
X
Y
1
1
1
2
1
1
3
2
1
4
2
1
5
2
1
6
3
1
As it should be after the script:
id
X
Y
1
1
1
2
1
2
3
2
1
4
2
2
5
2
3
6
3
1
I tried with row_number() but in the loop, it modify all the rows :
With a counter and variable to increment:
UPDATE dbo.A
SET "Y" = #MyInc
FROM (
SELECT ROW_NUMBER() OVER ( "Id" ASC) AS row_num_Id
, Id
, X
, Y
FROM dbo.A) AS sub
WHERE row_num_Id = #MyCounter;

This will give you the results you want
CREATE TABLE #T (
Id INT NOT NULL,
X INT NOT NULL,
Y INT NOT NULL
)
INSERT INTO #T(Id, X, Y)
VALUES
(1, 1, 1),
(2, 1, 1),
(3, 2, 1),
(4, 2, 1),
(5, 2, 1),
(6, 3, 1);
GO
WITH WithRowNumbers AS (
SELECT
Id,
X,
ROW_NUMBER() OVER (PARTITION BY X ORDER BY Id) As RowNumber
FROM #T
)
UPDATE T
SET Y = WRN.RowNumber
FROM WithRowNumbers AS WRN
INNER JOIN #T AS T ON T.Id = WRN.Id
SELECT * FROM #T
Or as #CharlieFace mentions you can simplify even more, as the CTE is like a view of the original table.
UPDATE T
SET Y = T.RowNumber
FROM WithRowNumbers AS T;

Related

How to create rows based on the range of all values between min and max in Snowflake (SQL)?

Assume I have the following data:
ID
T_Min
T_Max
1
3
5
2
1
4
I would like to create the following table using SQL (Snowflake):
ID
T
1
3
1
4
1
5
2
1
2
2
2
3
2
4
Does someone know how to do this? Thank you very much in advance!
Sample data:
CREATE OR REPLACE TABLE T1 (
ID INT,
T_Min INT,
T_Max INT);
INSERT INTO T1(ID, T_Min, T_Max)
SELECT * FROM VALUES (1, 3, 5), (2, 1, 4) t(ID, T_Min, T_Max);
Solution:
WITH N AS (
SELECT ROW_NUMBER() OVER(ORDER BY SEQ4()) AS T FROM TABLE(GENERATOR(ROWCOUNT => 1000)) -- Set to the maximum value of the difference between T_Max and T_Min
)
SELECT T1.ID, N.T
FROM T1
JOIN N ON N.T BETWEEN T1.T_Min AND T1.T_Max
ORDER BY T1.ID, N.T;

Match rows that include one of each at least once in SQL

I have a users table:
ID Name OID TypeID
1 a 1 1
2 b 1 2
3 c 1 3
4 d 2 1
5 e 2 1
6 f 2 2
7 g 3 2
8 h 3 2
9 i 3 2
for this table, I want to filter by OID and TypeID so that I get the rows that it is filtered by OID and that includes all 1, 2, and 3 in TypeID.
For example, where OID=1, we have 1, 2, and 3 in TypeID but I shouldn't get the rows with IDs 4-6 because for IDs 4-6, OIDs are the same but TypeID does not include all of each(1, 2, and 3).
You can do :
select oid
from table t
where typeid in (1,2,3)
group by oid
having count(*) = 3;
If, oid contain duplicate typeid then you can use count(distinct typeid) instead.
you could use exists
select oid from table t1
where exists ( select 1 from table t1 where t1.oid=t2.oid
group by t2.oid
having (distinct TypeID)=3
)
Asume TypeID 1,2,3
if you are using sql-server, you can try this.
DECLARE #SampleData TABLE(ID INT, Name VARCHAR(5), OID INT, TypeID INT)
INSERT INTO #SampleData VALUES
(1 , 'a', 1, 1),
(2 , 'b', 1, 2),
(3 , 'c', 1, 3),
(4 , 'd', 2, 1),
(5 , 'e', 2, 1),
(6 , 'f', 2, 2),
(7 , 'g', 3, 2),
(8 , 'h', 3, 2),
(9 , 'i', 3, 2)
SELECT * FROM #SampleData D
WHERE NOT EXISTS (
SELECT * FROM #SampleData D1
RIGHT JOIN (VALUES (1),(2),(3)) T(TypeID) ON D1.TypeID = T.TypeID
AND D.OID = D1.OID
WHERE D1.TypeID IS NULL
)
Result:
ID Name OID TypeID
----------- ----- ----------- -----------
1 a 1 1
2 b 1 2
3 c 1 3

SQL Server : set a row value based on a condition

I don't know what would be the appropriate title for this problem, but here is what I need to accomplish
Here is my dataset:
State TimeInState
--------------------------
1 20
3 0
4 5
8 2
5 10
1 18
3 30
12 2
2 0
What I want is another column in here, lets say FooID. What FooID is a int value that will remain same until the state is 1 again.
So the dataset would look like this:
State TimeInState FooID
------------------------------------------
1 20 1
3 0 1
4 5 1
8 2 1
5 10 1
1 18 2
3 30 2
12 2 2
2 0 2
So if there was another row at the end with State=1 then FooID will be 3 until the next state is changed.
How can I accomplish this in T-SQL?
Thanks in advance.
If you have some way of ordering rows (like an ID of sorts), then here is an example of how you could do something like this:
DECLARE #T TABLE (ID INT IDENTITY(1, 1), State INT, TimeInState INT)
INSERT #T (State, TimeInState)
VALUES (1, 20), (3, 0), (4, 5), (8, 2), (5, 10), (1, 18)
, (3, 30), (12, 2), (2, 0), (1, 1), (1, 1), (2, 1);
WITH CTE AS (
SELECT *
, ROW_NUMBER() OVER (ORDER BY CASE WHEN State = 1 THEN 0 ELSE 1 END, ID) RN
FROM #T
)
SELECT State, TimeInState, Foo.FooID
FROM CTE T
CROSS APPLY (SELECT MAX(RN) FooID FROM CTE WHERE State = 1 AND ID <= T.ID) Foo
ORDER BY ID;
But if you don't have the data ordered in some way already, then I don't think you can ensure the result set will sort the data in the way you want to sort it.

SQL aggregation on the latest output per machine for each time

I have the following table:
ID machine app output time
1 1 A 12 1
2 1 B 15 1
3 1 B 8 3
4 1 A 11 4
5 2 C 14 4
6 2 D 17 4
For each app I want to get the latest output given up to each point in time, and aggregate these results grouped by machine using AVG
So for the table on top, the data before aggregation should be:
time machine app latest
1 1 A 12
1 1 B 15
3 1 A 12
3 1 B 8
4 1 A 11
4 1 B 8
4 2 C 14
4 2 D 17
And the aggregated result should be:
time machine avg
1 1 =(12+15)/2
3 1 =(12+8)/2
4 1 =(11+8)/2
4 2 =(14+17)/2
What is the correct way to approach this problem?
It is not as simple as I thought to be, but I think it works just as You want. I changed time column to ts, like this:
CREATE TABLE Table1
(ID int, machine int, app char(1), output int, ts int)
;
INSERT INTO Table1
(ID,machine,app,output, ts)
VALUES
(1, 1, 'A', 12, 1),
(2, 1, 'B', 15, 1),
(3, 1, 'B', 8, 3),
(4, 1, 'A', 11, 4),
(5, 2, 'C', 14, 4),
(6, 2, 'D', 17, 4)
;
And here is the query:
WITH
times as
(
SELECT distinct ts FROM Table1
),
machine_apps as
(
SELECT DISTINCT machine,app FROM Table1
),
grid as
(
SELECT
ts,machine,app
FROM
times
CROSS JOIN machine_apps
),
last_outputs as
(
SELECT
g.ts,
g.app,
g.machine,
max(t.ts) as last_time
FROM
grid g
JOIN Table1 t ON (t.app = g.app AND t.machine = g.machine AND t.ts <= g.ts)
GROUP BY
g.ts,
g.app,
g.machine
)
SELECT
l.ts,
l.machine,
AVG(t.output) as avg
FROM
last_outputs l
LEFT JOIN Table1 t ON (t.app = l.app AND t.machine = l.machine AND t.ts = l.last_time)
GROUP BY
l.ts,
l.machine
ORDER BY
l.ts,
l.machine

MIcrosoft SQL Server WHERE/ CASE clauses

I have a where statement that depends on an id and based off the id the next where is determined. EX: if ID = 1 the where statement should be a<= 3 and b between 4 and 7 if ID <> 1 the where statement should be a<= 4 and b between 5 and 7. Not sure how to do this. Tried a Case clause but had no luck.
Here is a sample table in tempdb with data.
-- Just a test
use tempdb;
go
-- Drop table
if object_id('test') > 0
drop table test
go
-- Create table
create table test
(
id int,
a int,
b int
);
-- Add data
insert into test values
(1, 3, 4),
(2, 4, 5),
(1, 4, 4),
(2, 5, 5),
(1, 3, 3),
(2, 4, 4);
-- Full table
select * from test;
Here is a solution using the CASE statement.
-- Show the data
select
*
from
test
where
(
case
when id = 1 and a <= 3 and b between 4 and 7 then 1
when id <> 1 and a <= 4 and b between 5 and 7 then 1
else 0
end
) = 1;
Something like:
where
(id = 1 and a <= 3 and b between 4 and 7) or
(id <> 1 and a <= 4 and b between 5 and 7)
Based on your requirements you just need to parenthetical WHERE statements with an OR:
...
WHERE (ID = 1 AND a <= 3 AND b BETWEEN 4 AND 7)
OR (ID <> 1 AND a<= 4 AND b BETWEEN 5 AND 7)