I'm fairly new to sql and was hoping that someone can help me with an update query. I have a users table with group_id (foreign key), user_id and user_index columns. There are multiple users corresponding to each individual group_id, and user_id is a serial column which goes from 1 to the table size.
I'm looking for a query that will update the user_index column so that, for each group_id, each user will have a unique, sequential index starting with 1. So within group 1 there would be user_index 1,2,3... and within group 2 there would be user_index 1,2,3... and so on. Here is an example to clarify:
Initial state:
user_id | group_id | user_index
1 1 0
2 1 0
3 1 0
4 2 0
5 3 0
6 3 0
Desired state:
user_id | group_id | user_index
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
6 3 2
I hope that's clear. This would be easy to do in C or C++, but I'm wondering if there's a way to do it in sql.
UPDATE TableName
SET user_index = (SELECT COUNT(1) FROM TableName t2
WHERE t2.group_id = TableName.group_id AND t2.user_id <= TableName.user_id)
EDIT:
After looking at author's comment I created a test to see is this the right solution. Here's the test:
CREATE TABLE #table (user_id int, group_id int, user_index int)
INSERT INTO #table VALUES (1, 1, 0)
INSERT INTO #table VALUES (2, 1, 0)
INSERT INTO #table VALUES (3, 1, 0)
INSERT INTO #table VALUES (4, 2, 0)
INSERT INTO #table VALUES (5, 3, 0)
INSERT INTO #table VALUES (6, 3, 0)
SELECT * FROM #table
UPDATE #table
SET user_index = (SELECT COUNT(1) FROM #table t2
WHERE t2.group_id = #table.group_id AND t2.user_id <= #table.user_id)
SELECT * FROM #table
DROP TABLE #table
The output of two selects are exactly the same as in author's two selects - the first as beginning state, and the second as the desired outcome.
Related
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;
I want to add a column to my select showing a set of number from say 1 to 4.
Example:
Select * gives me
Id Transaction
1 10
2 11
3 12
4 13
5 14
6 15
I want to add a column called "Flow". The result should be like this.
Id Transaction Flow
1 10 1
2 11 2
3 12 3
4 13 4
5 14 1
6 15 2
In this example the flow is from 1-4. Could be 1-n.
No particular relation between Id and Flow is needed.
If you're using SQL Server or other DBMS that allows ROW_NUMBER, you could do this:
CREATE TABLE #Tbl(Id INT, [Transaction] INT);
INSERT INTO #Tbl VALUES
(1, 10), (2, 11), (3, 12), (4, 13), (5, 14), (6, 15);
DECLARE #N INT = 4;
SELECT *,
Flow = 1 + ((ROW_NUMBER() OVER(ORDER BY Id) - 1) % #N)
FROM #Tbl
DROP TABLE #Tbl;
If you are using mySql.
Query
set #r := 0;
select Id, `Transaction`,
#r := (#r % 4) + 1 as Flow
from your_table_name
order by Id;
Demo
EDIT
Following sql query can be used irrespective of rdbms.
Query
select *, (
select ((count(*) - 1) % 4) + 1 as Flow
from your_table_name t2
where t1.Id >= t2.Id
) as Flow
from your_table_name t1;
Say there is a SQL Server table which contain 2 columns: ID, Value
The sample data looks like this:
ID value
------------------
1 30
1 30
2 50
2 50
3 50
When I run this query:
select ID, NEWID(), value
from table1
order by ID
The result looks like this:
1 30 E152AD19-9920-4567-87FF-C4822FD9E485
1 30 54F28C58-ABA9-4DFB-9A80-CE9C4C390CBB
2 50 ........
2 50 ........
3 50 4E5A9E26-FEEC-4CC7-9AC5-96747053B6B2
But what I want is : how many record of ID depending on (sum of value /30 )'s result, for example of ID 2, it's value's sum is 50+50=100, and 100/30=3, so ID 2 will display in query result three times
The final result i want is like this:
1 E152AD19-9920-4567-87FF-C4822FD9E485
1 54F28C58-ABA9-4DFB-9A80-CE9C4C390CBB
2 4E5A9E26-FEEC-4CC7-9AC5-96747053B6B2
2 ....
2 ....
3 D861563E-E01A-4198-9E92-7BEB4678E5D1
Please note ID of 2 display three times, wait for your helps, thanks.
How about something like
CREATE TABLE Table1
([ID] int, [value] int)
;
INSERT INTO Table1
([ID], [value])
VALUES
(1, 30),
(1, 30),
(2, 50),
(2, 50),
(3, 50)
;
;WITH SummedVals AS (
SELECT ID,
SUM(value) / 30 Cnt
FROM Table1
GROUP BY ID
)
, Vals AS (
SELECT ID,
Cnt - 1 Cnt
FROM SummedVals
UNION ALL
SELECT ID,
Cnt - 1 Cnt
FROM Vals
WHERE Cnt > 0
)
SELECT ID,
NEWID()
FROM Vals
ORDER BY 1
SQL Fiddle DEMO
I have a table with an ID column and another column with a number. One ID can have multiple numbers. For example
ID | Number
1 | 25
1 | 26
1 | 30
1 | 24
2 | 4
2 | 8
2 | 5
Now based of this data, in a new table, I want to have this
ID | Low | High
1 | 24 | 26
1 | 30 | 30
2 | 4 | 5
2 | 8 | 8
As you can see, I want to merge any data where the numbers are consecutive, like 24, 25, 26. So now the low was 24, the high was 26, and then 30 is still a separate range. I am dealing with large amounts of data, so I would prefer to not use a cursor for performance sake (which is what I was previously doing, and was slowing things down quite a bit)...What is the best way to achieve this? I'm no SQL pro, so I'm not sure if there is a function available that could make this easier, or what the fastest way to accomplish this would be.
Thanks for the help.
The key observation is that a sequence of numbers minus another sequence is a constant. We can generate another sequence using row_number. This identifies all the groups:
select id, MIN(number) as low, MAX(number) as high
from (select t.*,
(number - ROW_NUMBER() over (partition by id order by number) ) as groupnum
from t
) t
group by id, groupnum
The rest is just aggregation.
Solution with CTE and recursion:
WITH CTE AS (
SELECT T.ID, T.NUMBER, T.NUMBER AS GRP
FROM T
LEFT OUTER JOIN T T2 ON T.ID = T2.ID AND T.NUMBER -1 = T2.NUMBER
WHERE T2.ID IS NULL
UNION ALL
SELECT T.ID, T.NUMBER, GRP
FROM CTE
INNER JOIN T
ON T.ID = CTE.ID AND T.NUMBER = CTE.NUMBER + 1
)
SELECT ID, MAX( NUMBER ), MIN(NUMBER)
FROM CTE
GROUP BY ID, GRP
Results at fiddlesql
I'd suggest using a WHILE loop structure with a table variable instead of the cursor.
For example,
DECLARE #TableVariable TABLE
(
MyID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
[ID] int,
[Number] int
)
DECLARE #Count int, #Max int
INSERT INTO #TableVariable (ID, Number)
SELECT ID, Number
FROM YourSourceTable
SELECT #Count = 1, #Max = MAX(MyID)
FROM #TableVariable
WHILE #Count <= #Max
BEGIN
...do your processing here...
SET #Count = #Count + 1
END
CREATE TABLE Table1
([ID] int, [Number] int)
;
INSERT INTO Table1
([ID], [Number])
VALUES
(1, 25),
(1, 26),
(1, 30),
(1, 24),
(2, 4),
(2, 8),
(2, 5)
;
select ID,
MIN(Number)
,(SELECT MIN(Number)
FROM (SELECT TOP 2 Number from Table1 WHERE ID =
T1.Id ORDER BY Number DESC) as DT)
from Table1 as T1
GROUP BY ID
UNION
SELECT ID, MAX(Number), MAX(Number)
FROM Table1 as T1
GROUP BY ID;
Live Example
I have a set of records.
ID Value
1 a
2 b
3 b
4 b
5 a
6 a
7 b
8 b
And I would like to group them like so.
MIN(ID) MAX(ID) Value
1 1 a
2 4 b
5 6 a
7 8 b
I'm vaguely aware of oracle over() analytical function which looks to be the right direction, but I don't know what this problem is called much less how to solve it.
Probably an easier way, but this may help to start. I ran it on Postgres, but should work (maybe with a minor tweak) on Oracle. The inner most query puts the previous value on each row. We can use that to detect a grouping change (when value does not equal previous value). Every time there is a group change, we flag it with a "1". Sum these group changes and we now have a group id which increments every time there is a value change. Then we can perform our normal group by function.
create table x(id int, value varchar(1));
insert into x values(1, 'a');
insert into x values(2, 'b');
insert into x values(3, 'b');
insert into x values(4, 'b');
insert into x values(5, 'a');
insert into x values(6, 'a');
insert into x values(7, 'b');
insert into x values(8, 'b');
SELECT MIN(id), MAX(id), value
FROM ( SELECT id
,value
,previous_value
,SUM( CASE WHEN value = previous_value THEN 0 ELSE 1 END ) OVER(ORDER BY id) AS group_id
FROM ( SELECT id
,value
,COALESCE( LAG(value) OVER(ORDER BY id), value ) previous_value
FROM x
ORDER BY id
) y
) z
GROUP BY group_id, value
ORDER BY 1, 2;
min | max | value
-----+-----+-------
1 | 1 | a
2 | 4 | b
5 | 6 | a
7 | 8 | b
(4 rows)