how to reset the column value - sql

i have table with 9 records that 2 records with _id = 1 was deleted:
id | name | index | _id
1 a 1 1
8 b 3 1
9 c 7 1
10 d 4 1
15 e 2 1
16 d 1 2
17 e 2 2
and I want to reset the index of _id = 1 like this:
id | name | index | _id
1 a 1 1
8 b 2 1
9 c 3 1
10 d 4 1
15 e 5 1
16 d 1 2
17 e 2 2
i 'd use this query
declare #_idCount int = (select count(*) from tbl where _id = 1),
#index int = 1
while(#_idCount > 0)
begin
update tbl
set code = #index
where _id = 1
set #index = #index + 1
set #picCount = #picCount - 1
end
but this code is set all of record with same code.
what can i do to solve it?

You can simply use ROW_NUMBER:
WITH CTE AS
(
SELECT id,
name,
[index],
[_id],
new_index = ROW_NUMBER() OVER(PARTITION BY [_id]
ORDER BY [id])
FROM dbo.tbl
)
UPDATE CTE
SET [index] = new_index;

Related

Update a column if a row is not a duplicate, or a row's ID isn't a certain number in SQL SSMS

I have a table, and I would like the following update command to set isExcluded = 1 to all rows where PhoneID and PhoneName are not duplicates and all rows where the ID doesn't not have the smallest number from a selected PhoneID if those rows do not have a duplicate PhoneID and PhoneName (i.e.: since all rows with PhoneID = 2 are not duplicates, the row containing PhoneName = b has the smallest ID, since it's ID = 3. Therefore, all rows with PhoneID = 2 and ID > 3 will have their IsExcluded set to 1).
ID
PhoneID
PhoneName
isExcluded
1
1
a
0
2
1
a
0
3
2
b
0
4
2
c
0
5
2
d
0
6
2
e
0
7
3
c
0
8
3
c
0
9
3
d
0
10
3
d
0
Here's my SQL script that I wrote. It only seems to get the non-duplicates only.
WITH Duplicates AS
(
SELECT
ID, PhoneID, PhoneName, isExcluded,
(ROW_NUMBER() OVER (PARTITION BY PhoneName, PhoneID ORDER BY ID)) AS RowNum
FROM
Phones
)
UPDATE Phones
SET isExcluded = 1
FROM Duplicates d
WHERE (
d.PhoneID = Phones.PhoneID
AND d.PhoneName = Phones.PhoneName
AND d.RowNum =< 1);
SELECT * FROM Phones;
This table should be the result of my command.
ID
PhoneID
PhoneName
isExcluded
1
1
a
0
2
1
a
0
3
2
b
0
4
2
c
1
5
2
d
1
6
2
e
1
7
3
c
0
8
3
c
0
9
3
d
1
10
3
d
1
This looks to be a variation of a gaps and islands problem, which you can solve by first grouping the partitions and then using an updatable CTE to assign the isExcluded value
with gp as (
select *,
Row_Number() over(order by id)
- Row_Number() over(partition by phoneid, phonename order by id) gp
from t
), p as (
select *,
case when Min(gp) over(partition by phoneid) <gp then 1 end IsEx
from gp
)
update p set isExcluded = isEx
where IsEx = 1
See working DB<>Fiddle

SQL: subset data: select id when time_id for id satisfy a condition from another column

I have a data (dt) in SQL like the following:
ID time_id act rd
11 1 1 1
11 2 4 1
11 3 7 0
12 1 8 1
12 2 2 0
12 3 4 1
12 4 3 1
12 5 4 1
13 1 4 1
13 2 1 0
15 1 3 1
16 1 8 0
16 2 8 0
16 3 8 0
16 4 8 0
16 5 8 0
and I want to take the subset of this data such that only ids (and their corresponding time_id, act, rd) that has time_id == 5 is retained. The desired output is the following
ID time_id act rd
12 1 8 1
12 2 2 0
12 3 4 1
12 4 3 1
12 5 4 1
16 1 8 0
16 2 8 0
16 3 8 0
16 4 8 0
16 5 8 0
I know I should use having clause somehow but have not been successful so far (returns me empty outputs). below is my attempt:
SELECT * FROM dt
GROUP BY ID
Having min(time_id) == 5;
This query:
select id from tablename where time_id = 5
returns all the ids that you want in the results.
Use it with the operator IN:
select *
from tablename
where id in (select id from tablename where time_id = 5)
You can use a correlated subquery with exists:
select t.*
from t
where exists (select 1 from t t2 where t2.id = t.id and t2.time_id = 5);
WITH temp AS
(
SELECT id FROM tab WHERE time_id = 5
)
SELECT * FROM tab t join temp tp on(t.id=tp.id);
check this query
select * from table t1 join (select distinct ID from table t where time_id = 5) t2 on t1.id =t2.id;

SQL recursive hierarchy

I am struggling to get one recursive CTE to work as desired but still with no chance..
So, I have the following similar table structures:
tblMapping:
map_id | type_id | name | parent_id
1 1 A1 0
2 1 A2 0
3 1 A3 1
4 1 A4 3
5 2 B1 0
6 2 B2 5
7 2 B3 6
8 1 A5 4
9 2 B4 0
tblRoleGroup:
role_group_id | type_id | map_id | desc_id
1 1 0 null
1 2 0 null
2 1 3 1
2 2 6 0
3 1 8 1
3 2 9 1
In tblRoleGroup, the desc_id field means:
null - allow all (used only in combination with map_id=0)
0 - allow all from parent including parent
1 - allow only current node
Still in tblRoleGroup if map_id=0 then the query should get all elements from same type_id
The query result should look like this:
role_group_id | type_id | map_id | path
1 1 1 A1
1 1 2 A2
1 1 3 A1.A3
1 1 4 A1.A3.A4
1 1 8 A1.A3.A4.A5
1 2 5 B1
1 2 6 B1.B2
1 2 7 B1.B2.B3
1 2 9 B4
2 1 3 A1.A3
2 2 6 B1.B2
2 2 7 B1.B2.B3
3 1 8 A1.A3.A4.A5
3 2 9 B4
The query below solves only a part of the expected result, but I wasn't able to make it work as the expected result..
WITH Hierarchy(map_id, type_id, name, Path) AS
(
SELECT t.map_id, t.type_id, t.name, CAST(t.name AS varchar(MAX)) AS Expr1
FROM dbo.tblMapping AS t
LEFT JOIN dbo.tblMapping AS t1 ON t1.map_id = t.parent_id
WHERE (t1.parent_id=0)
UNION ALL
SELECT t.map_id, t.type_id, t.name, CAST(h.Path + '.' + t.name AS varchar(MAX)) AS Expr1
FROM Hierarchy AS h
JOIN dbo.tblMapping AS t ON t.parent_id = h.map_id
)
SELECT h.map_id, h.type_id, t.role_group_id, h.Path AS Path
FROM Hierarchy AS h
LEFT JOIN dbo.tblRoleGroup t ON t.map_id = h.map_id
Could someone help me on this?
Thank you
At first I create a function that brings all descendants of passed map_id:
CREATE FUNCTION mapping (#map_id int)
RETURNS TABLE
AS
RETURN
(
WITH rec AS (
SELECT map_id,
[type_id],
CAST(name as nvarchar(max)) as name,
parent_id
FROM tblMapping
WHERE map_id = #map_id
UNION ALL
SELECT m.map_id,
m.[type_id],
r.name+'.'+m.name,
m.parent_id
FROM rec r
INNER JOIN tblMapping m
ON m.parent_id = r.map_id
)
SELECT *
FROM rec
);
GO
Then run this:
;WITH rec AS (
SELECT map_id,
[type_id],
CAST(name as nvarchar(max)) as name,
parent_id
FROM tblMapping
WHERE parent_id=0
UNION ALL
SELECT m.map_id,
m.[type_id],
r.name+'.'+m.name,
m.parent_id
FROM rec r
INNER JOIN tblMapping m
ON m.parent_id = r.map_id
)
SELECT t.role_group_id,
r.[type_id],
r.map_id,
r.name as [path]
FROM tblRoleGroup t
CROSS JOIN rec r
WHERE r.[type_id] = CASE WHEN t.desc_id IS NULL AND t.map_id = 0 THEN t.[type_id] ELSE NULL END
OR r.map_id = CASE WHEN t.desc_id = 1 THEN t.map_id ELSE NULL END
OR r.map_id IN (
SELECT map_id
FROM dbo.mapping (CASE WHEN t.desc_id = 0 THEN t.map_id ELSE NULL END)
)
ORDER BY role_group_id, r.[type_id], r.map_id
Will give you:
role_group_id type_id map_id path
1 1 1 A1
1 1 2 A2
1 1 3 A1.A3
1 1 4 A1.A3.A4
1 1 8 A1.A3.A4.A5
1 2 5 B1
1 2 6 B1.B2
1 2 7 B1.B2.B3
1 2 9 B4
2 1 3 A1.A3
2 2 6 B1.B2
2 2 7 B1.B2.B3
3 1 8 A1.A3.A4.A5
3 2 9 B4

SQL Server 2008 Cumulative Sum that resets value

I want to have the last column cumulative based on ROW_ID that resets every time it starts again with '1'.
Initially my table doesn't have the ROW_ID, this was created using partition so at least I can segregate my records.
It should add the Amt + CumulativeSum (except for the first record) all the way down and reset every time the Row_ID = 1.
I have tried several queries but it doesn't give me the desired result. I am trying to read answers from several forums but to no avail.
Can someone advise the best approach to do this?
For the sake of representation, I made the sample table as straightforward as possible.
ID ROW-ID Amt RunningTotal(Amt)
1 1 2 2
2 2 4 6
3 3 6 12
4 1 2 2
5 2 4 6
6 3 6 12
7 4 8 20
8 5 10 30
9 1 2 2
10 2 4 6
11 3 6 12
12 4 8 20
try this
declare #tb table(ID int, [ROW-ID] int, Amt money)
insert into #tb(ID, [ROW-ID], Amt) values
(1,1,2),
(2,2,4),
(3,3,6),
(4,1,2),
(5,2,4),
(7,4,8),
(8,5,10),
(9,1,2),
(10,2,4),
(11,3,6),
(12,4,8)
select *,sum(amt) over(partition by ([id]-[row-id]) order by id,[row-id]) AS cum from #tb
other version
select *,(select sum(amt) from #tb t where
(t.id-t.[row-id])=(t1.id-t1.[ROW-ID]) and (t.id<=t1.id) ) as cum
from #tb t1 order by t1.id,t1.[row-id]
Try this
SELECT distinct (T1.ID),
T1.ROW_ID,
T1.Amt,
CumulativeSum =
CASE
WHEN T1.RoW_ID=1 THEN T1.Amt
ELSE T1.Amt+ T2.Amt
END
FROM TestSum T1, TestSum T2
WHERE T1.ID = T2.ID+1
http://sqlfiddle.com/#!6/8b2a2/2
The idea is to create partitions from R column. First leave 1 if R = 1, else put 0. Then cumulative sum on that column. When you have partitions you can finally calculate cumulative sums on S column in those partitions:
--- --- ---
| 1 | | 1 | | 1 |
| 2 | | 0 | | 1 | --prev 1 + 0
| 3 | | 0 | | 1 | --prev 1 + 0
| 1 | | 1 | | 2 | --prev 1 + 1
| 2 | => | 0 | => | 2 | --prev 2 + 0
| 3 | | 0 | | 2 | --prev 2 + 0
| 4 | | 0 | | 2 | --prev 2 + 0
| 5 | | 0 | | 2 | --prev 2 + 0
| 1 | | 1 | | 3 | --prev 2 + 1
| 2 | | 0 | | 3 | --prev 3 + 0
--- --- ---
DECLARE #t TABLE ( ID INT, R INT, S INT )
INSERT INTO #t
VALUES ( 1, 1, 2 ),
( 2, 2, 4 ),
( 3, 3, 6 ),
( 4, 1, 2 ),
( 5, 2, 4 ),
( 6, 3, 6 ),
( 7, 4, 8 ),
( 8, 5, 10 ),
( 9, 1, 2 ),
( 10, 2, 4 ),
( 11, 3, 6 ),
( 12, 4, 8 );
For MSSQL 2008:
WITH cte1
AS ( SELECT ID ,
CASE WHEN R = 1 THEN 1
ELSE 0
END AS R ,
S
FROM #t
),
cte2
AS ( SELECT ID ,
( SELECT SUM(R)
FROM cte1 ci
WHERE ci.ID <= co.ID
) AS R ,
S
FROM cte1 co
)
SELECT * ,
( SELECT SUM(S)
FROM cte2 ci
WHERE ci.R = co.R
AND ci.ID <= co.ID
)
FROM cte2 co
For MSSQL 2012:
WITH cte
AS ( SELECT ID ,
SUM(CASE WHEN R = 1 THEN 1
ELSE 0
END) OVER ( ORDER BY ID ) AS R ,
S
FROM #t
)
SELECT * ,
SUM(s) OVER ( PARTITION BY R ORDER BY ID ) AS T
FROM cte
Output:
ID R S T
1 1 2 2
2 1 4 6
3 1 6 12
4 2 2 2
5 2 4 6
6 2 6 12
7 2 8 20
8 2 10 30
9 3 2 2
10 3 4 6
11 3 6 12
12 3 8 20
EDIT:
One more way. This looks way better by execution plan then first example:
SELECT * ,
CASE WHEN R = 1 THEN S
ELSE ( SELECT SUM(S)
FROM #t it
WHERE it.ID <= ot.ID
AND it.ID >= ( SELECT MAX(ID)
FROM #t iit
WHERE iit.ID < ot.ID
AND iit.R = 1
)
)
END
FROM #t ot

select multiple count set, sql server 2008 r2

Using SQL Server 2008 R2, i have following result set returned by a query -
QID QcID QtID QsID
21 1 SC 3
4 1 SC 1
8 1 MC 1
2 1 SC 1
23 1 SC 3
24 1 SC 3
5 1 SC 1
22 1 SC 3
1 1 SC 1
29 1 MC 3
10 1 MC 1
30 1 MC 3
26 1 MC 3
25 1 SC 3
6 1 MC 1
27 1 MC 3
7 1 MC 1
3 1 SC 1
28 1 MC 3
9 1 MC 1
Now i want to find a random set of 15 QID, which must includes say -
9 QsID having QsID = 1
6 QsID having QsID = 3
9 QtID having QtID = SC
6 QtID having QtID = MC
15 QsID having QtID = 1
how it can be done keeping performance in mind as it may have tens of thousand records.
# Damien_The_Unbeliever the expected output could be -
21 1 SC 3
4 1 SC 1
8 1 MC 1
2 1 SC 1
23 1 SC 3
24 1 SC 3
5 1 SC 1
1 1 SC 1
10 1 MC 1
25 1 SC 3
6 1 MC 1
27 1 MC 3
7 1 MC 1
3 1 SC 1
28 1 MC 3
Also forget the random, how it be possible to select the set satisfying all the conditions.
EDIT2:
Well, how about using a stored procedure for that?
Assumption is that you have 4 sets of conditions you sample data with. Deduced from the output you provided. It might not be right but then again you can adjust it as you like.
Parameters:
#SIZE - is the size of sampling result set in rows
#P1-#P3 - percent of the sampling result set that should be filled with random rows for particular set of conditions.
#P4=#SIZE-(#N1+#N2+#N3)
CREATE PROCEDURE sqlsampling
#SIZE INT, #P1 DECIMAL(6,4), #P2 DECIMAL(6,4), #P3 DECIMAL(6,4)
AS
DECLARE #N1 INT, #N2 INT, #N3 INT, #N4 INT;
SET #N1=CEILING(#SIZE*#P1*0.01);
SET #N2=CEILING(#SIZE*#P2*0.01);
SET #N3=CEILING(#SIZE*#P3*0.01);
SET #N4=#SIZE-(#N1+#N2+#N3);
CREATE TABLE #sample(QID INT, QcID INT, QtID CHAR(2), QsID INT);
INSERT INTO #sample
SELECT TOP(#N1) * FROM mytable
WHERE QtID = 'MC' AND QsID = 1
ORDER BY CHECKSUM(NEWID());
INSERT INTO #sample
SELECT TOP(#N2) * FROM mytable
WHERE QtID = 'MC' AND QsID = 3 AND QID NOT IN(SELECT QID FROM #sample)
ORDER BY CHECKSUM(NEWID());
INSERT INTO #sample
SELECT TOP(#N3) * FROM mytable
WHERE QtID = 'SC' AND QsID = 1 AND QID NOT IN(SELECT QID FROM #sample)
ORDER BY CHECKSUM(NEWID());
INSERT INTO #sample
SELECT TOP(#N4) * FROM mytable
WHERE QtID = 'SC' AND QsID = 3 AND QID NOT IN(SELECT QID FROM #sample)
ORDER BY CHECKSUM(NEWID());
SELECT * FROM #sample;
DROP TABLE #sample;
GO
If we execute it on your sample data like this
EXEC sqlsampling #SIZE=15, #P1=26.666, #P2=13.333, #P3=33.333;
It will give us the output:
QID QCID QTID QSID
10 1 MC 1
9 1 MC 1
7 1 MC 1
6 1 MC 1
27 1 MC 3
30 1 MC 3
1 1 SC 1
4 1 SC 1
3 1 SC 1
5 1 SC 1
2 1 SC 1
21 1 SC 3
23 1 SC 3
25 1 SC 3
24 1 SC 3
Other considerations:
Correct indexes for your sets of conditions should help
CHECKSUM(NEWID()) helps us a bit with the penalty of using NEWID()
Original answer:
You can do something like this:
SELECT TOP 15 * FROM
(
SELECT * FROM (SELECT TOP 9 QID FROM mytable
WHERE QsID = 1
ORDER BY CHECKSUM(NEWID())) a
UNION
SELECT * FROM (SELECT TOP 6 QID FROM mytable
WHERE QsID = 3
ORDER BY CHECKSUM(NEWID())) b
...
) z ORDER BY CHECKSUM(NEWID())
Perhaps you may try percent with top.. this is not the complete answer, but to throw some light into the direction..
select * from demo where qid in
(select top 40 percent qid
from demo order by newid())
;
Here is also a Reference to tablesample: http://msdn.microsoft.com/en-us/library/ms189108.aspx