column update in sequence - sql

I have a table that contains 3 columns as below:
col1 col2 col3
---- ---- ----
1 1 null
2 2 null
3 3 null
4 1 null
5 1 null
6 1 null
7 2 null
ETC
I need to update a third column in the same table as follows:
col1 col2 col3
---- ---- ----
1 1 1
2 2 1
3 3 1
4 1 2
5 1 3
6 1 4
7 2 4
The logic behind the update is that each time the 2nd column contains a 1 in it, the third has to increment. The first column is just a sequential integer column.

You can use the row_number analytical function to number the rows with col2 = 1 sequentially and then use a subquery to find to closest value with a lower col1 for the other rows.
So given a test table like this:
create table t (c1 int, c2 int, c3 int);
insert t (c1, c2) values
(1, 1),
(2, 2),
(3, 3),
(4, 1),
(5, 1),
(6, 1),
(7, 2);
A query like this:
;with cte as (
select t.c1, t.c2, x.c3
from t
left join (
select c1, c2, row_number() over (partition by c2 order by c1) c3
from t
where c2 = 1
) x on t.c1 = x.c1
)
update t
set t.c3 = coalesce(cte1.c3,(select max(cte2.c3) from cte cte2 where cte2.c1 < cte1.c1))
from cte cte1
where t.c1 = cte1.c1
Will give the following result:
c1 c2 c3
1 1 1
2 2 1
3 3 1
4 1 2
5 1 3
6 1 4
7 2 4
Another, possibly faster, way to do this would be:
update t1 set c3 = (select count(*) from t where c2 = 1 and c1 <= t1.c1) from t t1

Related

SQL - Sum values when there is null

I have the following table:
RowID Column1 Column2
1 3 2
2 5 2
3 2 9
4 5 NULL
5 8 NULL
6 9 3
7 1 NULL
I need first row of Column1 to Sum every time there is a NULL value in Column2. And it would continue the logic down the rows.
So, the result should look like:
RowID Column1 Column2
1 3 2
2 5 2
3 15 9
4 5 NULL
5 8 NULL
6 10 3
7 1 NULL
Notice Row 3 summed 2+5+8 =15 and Row 6 summed 9+1 =10. So, basically the row prior to Null value in Column2 summed the values in column1 until there was no more NULL values in column2. Then it resumed in row 6 where the next value was NULL.
This will do it. I have set up the data in a table variable for demo.
declare #t table(RowID int, C1 int, C2 int)
insert #t values (1, 3, 2)
,(2, 5, 2)
,(3, 2, 9)
,(4, 5, NULL)
,(5, 8, NULL)
,(6, 9, 3)
,(7, 1, NULL)
select RowID, sum(C1), max(C2)
from (
select RowID, C1, C2 from #t
union all
select T1.RowID, T2.C1, null
from #t t1
join #t t2 on t2.RowID>t1.RowID and t2.C2 is null
and not exists(
select * from #t t3
where t3.RowID>t1.RowID and t3.c2 is not null and t3.RowID<t2.RowID
)
where T1.C2 is not null
) q group by RowID
Result:
RowID C1 C2
1 3 2
2 5 2
3 15 9
4 5 NULL
5 8 NULL
6 10 3
7 1 NULL
I got it. You need to look at the rows in reverse order, assigning the NULL values to the value before them.
The idea is to assign a group to the rows to sum. This is the number of non-NULL values following the row. With this, you can then use a window function to aggregate:
select t.*,
(case when c2 is null then c1
else sum(c1) over (partition by grp)
end) as new_c1
from (select t.*, count(c2) over (order by rowid rows between 1 following and unbounded following) as grp
from t
) t
order by rowid;
Here is a db<>fiddle.

Sql query group by(Get count using where condition) [duplicate]

This question already has answers here:
Count based on condition in SQL Server
(4 answers)
Closed 4 years ago.
Stuck in group by clause.
Col1 Col2
----------------
1 1
1 2
2 1
3 3
4 2
5 4
2 3
I want count of Col2 such that Col2 value is 1:
The output expected is:
Col1 Count(Col2)
---------------------
1 1
2 1
3 0
4 0
5 0
;with
d as (
select * from (values
(1, 1),
(1, 2),
(2, 1),
(3, 3),
(4, 2),
(5, 4),
(2, 3)) x (Col1, Col2)
)
select distinct d1.Col1, isnull(cnt, 0) cnt
from d d1
left join (
select Col1, COUNT(Col2) cnt
from d d1
where Col2=1
group by Col1, col2
) d2 on d1.Col1 = d2.Col1
order by 1,2
Output:
Col1 Cnt
1 1
2 1
3 0
4 0
5 0
You can use conditional aggregation:
select col1, sum(case when col2 = 1 then 1 else 0 end)
from t
group by col1;

MS SQL Set Group ID Without Looping

I would like create a query in MS-SQL to make a column containing an incrementing group number.
This is how I want my data to return:
Column 1 | Column 2 | Column 3
------------------------------
I | 1 | 1
O | 2 | 2
O | 2 | 3
I | 3 | 4
O | 4 | 5
O | 4 | 6
O | 4 | 7
O | 4 | 8
I | 5 | 9
O | 6 | 10
Column 1 is the I and O meaning In and Out.
Column 2 is the row Group (this should increment when Column 1 changes).
Column 3 is the Row-number.
So how can I write my query so that Column 2 increments every time Column 1 changes?
Firstly, to perform this kind of operation you need some column that can identify the order of the rows. If you have a column that determines this order, an identity column for example, it can be used to do something like this:
Runnable sample:
CREATE TABLE #Groups
(
id INT IDENTITY(1, 1) , -- added identity to provide order
Column1 VARCHAR(1)
)
INSERT INTO #Groups
( Column1 )
VALUES ( 'I' ),
( 'O' ),
( 'O' ),
( 'I' ),
( 'O' ),
( 'O' ),
( 'O' ),
( 'O' ),
( 'I' ),
( 'O' );
;
WITH cte
AS ( SELECT id ,
Column1 ,
1 AS Column2
FROM #Groups
WHERE id = 1
UNION ALL
SELECT g.id ,
g.Column1 ,
CASE WHEN g.Column1 = cte.Column1 THEN cte.Column2
ELSE cte.Column2 + 1
END AS Column2
FROM #Groups g
INNER JOIN cte ON cte.id + 1 = g.id
)
SELECT *
FROM cte
OPTION (MAXRECURSION 0) -- required to allow for more than 100 recursions
DROP TABLE #Groups
This code effectively loops through the records, comparing each row to the next and incrementing the value of Column2 if the value in Column1 changes.
If you don't have an identity column, then you might consider adding one.
Credit #AeroX:
With 30K records, the last line: OPTION (MAXRECURSION 0) is required to override the default of 100 recursions when using a Common Table Expression (CTE). Setting it to 0, means that it isn't limited.
This will work if you have sqlserver 2012+
DECLARE #t table(col1 char(1), col3 int identity(1,1))
INSERT #t values
('I'), ('O'), ('O'), ('I'), ('O'), ('O'), ('O'), ('O'), ('I'), ('O')
;WITH CTE AS
(
SELECT
case when lag(col1) over (order by col3) = col1
then 0 else 1 end increase,
col1,
col3
FROM #t
)
SELECT
col1,
sum(increase) over (order by col3) col2,
col3
FROM CTE
Result:
col1 col2 col3
I 1 1
O 2 2
O 2 3
I 3 4
O 4 5
O 4 6
O 4 7
O 4 8
I 5 9
O 6 10

Oracle query to select rows with unique code

I have a table like this
C1 C2 C3 Code
1 2 3 33
1 2 3 34
2 4 1 14
1 2 3 14
i want to select only those record whose code is appearing only in single row. ie, in this case rows with code 33 and 34.. as they appear only once in this table.
How can i write a query for that
If you want only one pass over your data, then you can use this query:
SQL> create table mytable (c1,c2,c3,code)
2 as
3 select 1, 2, 3, 33 from dual union all
4 select 1, 2, 3, 34 from dual union all
5 select 2, 4, 1, 14 from dual union all
6 select 1, 2, 3, 14 from dual
7 /
Table created.
SQL> set autotrace on
SQL> select max(c1) c1
2 , max(c2) c2
3 , max(c3) c3
4 , code
5 from mytable
6 group by code
7 having count(*) = 1
8 /
C1 C2 C3 CODE
---------- ---------- ---------- ----------
1 2 3 33
1 2 3 34
2 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 SORT (GROUP BY)
3 2 TABLE ACCESS (FULL) OF 'MYTABLE'
Regards,
Rob.
SELECT C1, C2, C3, Code FROM tablename
WHERE Code IN
(
SELECT Code FROM tablename
GROUP BY Code
HAVING count(Code) = 1
)
select C1, C2, C3, Code
from tablename T1
where not exists ( select T2.exclude
from tablename T2
where T2.Code = T1.Code
and T2.rowid <> T1.rowid
)
PS. Watch out for NULL values in the Code column

table join where

Table1 t10 (id)
id
---
1
2
table t11(a1,a2,a3)
a1 a2 a3
----------
1 10 a
1 10 b
1 11 b
1 12 c
2 20 d
2 21 e
select * from t10 a,t11 b where a.id = b.a1
how to display
id a1 a2 a3
--------------
1 1 10 a
1 1 10 b //(not display this row)
1 1 11 b //(not display this row)
1 1 12 c //(not display this row)
2 2 20 d
2 2 21 e //(not display this row)
just get t11's random row
maybe display this
id a1 a2
----------
1 1 11 b
1 1 10 a //(not display this row)
1 1 10 b //(not display this row)
1 1 12 c //(not display this row)
2 2 20
2 2 21 //(not display this row)
select a1 as id, a1, min(a2) as a2
from t11
group by a1
will give you:
id a1 a2
----------
1 1 10
2 2 20
This seems almost like he wants something like FIRST/LAST from ms access.
This can ben done (very closely) in Sql Server using
DECLARE #Table TABLE(
id INT,
a1 INT,
a2 INT
)
INSERT INTO #Table (id,a1,a2) SELECT 1, 1, 11
INSERT INTO #Table (id,a1,a2) SELECT 1, 1, 10
INSERT INTO #Table (id,a1,a2) SELECT 1, 1, 12
INSERT INTO #Table (id,a1,a2) SELECT 2, 2, 20
INSERT INTO #Table (id,a1,a2) SELECT 2, 2, 21
SELECT *
FROM #Table t
WHERE a2 = (SELECT TOP 1 a2 FROM #Table WHERE id = t.id AND a1 = t.a1)
This is the answer:
SELECT *
FROM t10 a, (
SELECT * FROM (
SELECT b.*, ROW_NUMBER() OVER(PARTITION BY a10 ORDER BY a10) as rn
FROM t11 b
) WHERE rn =1) b
WHERE a.id = b.a10(+)