SQL Update everytime column hits x number rows - sql-server

I have table call question with two columns, it contains more than 160K rows, example:
id | questionID
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
9 | 9
10 | 10
...
I would like to update the questionID column so it will look like the example below. For every x number rows it need update to set from 1 again. The final result should be something like this:
id | questionID
1 | 1
2 | 2
3 | 3
4 | 4
5 | 1
6 | 2
7 | 3
8 | 4
9 | 1
10 | 2
...
The table contains some many rows, so its not an option do it manually.
What could be the easiest way to update the table?
Any help will be appreciated. Thanks

If you are going to use the modulus operator. Both SQL Server and MySQL support %:
UPDATE question
SET questionID = 1 + ((id - 1) % 4);
If the numbers have gaps, then you need to do something different. In that case, the solution is highly database dependent.

Simply use modulo operator:
UPDATE question
SET questionID = CASE WHEN id % 4 = 0 THEN 4 ELSE id % 4 END
or, if id has gaps and you are using SQL Server, then you can use this:
UPDATE q1
SET id = (CASE WHEN q2.rn % 4 = 0 THEN 4 ELSE q2.rn % 4 END)
FROM question q1
INNER JOIN (
SELECT id, ROW_NUMBER() OVER (ORDER by id) AS rn
FROM question ) q2 ON q1.ID = q2.ID

UPDATE question SET questionID = questionID % 4 + 1

Related

Displaying data in a different manner

So I have a table that binds ProductId and GroupId. The product can be assigned to all of 5 groups (1-5).
If the product doesn't exist in the table, it's not assigned to any of the group
ProductId | GroupId
-------------------
100 | 1
100 | 2
200 | 1
200 | 2
200 | 3
200 | 4
200 | 5
Taking a look at this table, we know that Product that goes by id 100 is assigned to 2 groups (1,2) and the product of id 200 is assigned to 5 groups (1-5).
I'm trying to write a query that will display each product in separate row, together with columns for all of the 5 groups and a bit value that contains information if the product belongs to the group or not (0,1). A visualization of the result I need:
ProductId | IsGroup1 | IsGroup2 | IsGroup3 | IsGroup4 | IsGroup5
-----------------------------------------------------------------
100 | 1 | 1 | 0 | 0 | 0 -- this belongs to groups 1, 2
200 | 1 | 1 | 1 | 1 | 1 -- this belongs to all of the groups
I know I could probably solve it using a self join 5 times on each distinct product, but I'm wondering if there's a more elegant way of solving it?
Any tips will be strongly appreciated
You could use a pivot. Since you only have 5 groups you don't need a dynamic pivot.
DB FIDDLE
select
ProductId
,IsGroup1 = iif([1] is null,0,1)
,IsGroup2 = iif([2] is null,0,1)
,IsGroup3 = iif([3] is null,0,1)
,IsGroup4 = iif([4] is null,0,1)
,IsGroup5 = iif([5] is null,0,1)
from
(select ProductID, GroupId from mytable) x
pivot
(max(GroupId) for GroupId in ([1],[2],[3],[4],[5])) p

T-SQL: UPDATE table according to a column

TLDNR: how do I update a table depending on a column?
Problem situation: the current column SortingNumber is full of bad data.
Solution: reassign new values to SortingNumber based on their Parent. The SortingNumber shall be 1 for the lowest current SortingNumber (by Parent) and be incremented by 1 for every subsequent dataset.
Current data: Desired result:
ID | Parent | SortingNumber >> ID | Parent | SortingNumber
1 | 1 | 3 >> 1 | 1 | 1
2 | 1 | 4 >> 2 | 1 | 2
3 | 1 | 5 >> 3 | 1 | 3
4 | 2 | 8 >> 4 | 2 | 1
5 | 2 | 10 >> 5 | 2 | 2
6 | 2 | 13 >> 6 | 2 | 3
Actual problem: I'm having trouble figuring out how to update the datasets corresponding to their parents.
My script currently updates all the values incrementally and doesn't group it by Parent.
My current solution:
DECLARE #lastSN INTEGER = 0;
WITH toUpdate AS
(
SELECT
T1.*,
-- "calculate" the sorting number from the row above
LAG(T1.SortingNumber + 1, 1, 1) OVER (ORDER BY T1.SortingNumber) AS [newSortNumber]
FROM
T AS T1
INNER JOIN
T AS T2 ON T1.Parent = T2.ID
)
UPDATE toUpdate
SET
#lastSN = CASE WHEN [newSortNumber] = 1 AND #lastSN = 0 THEN 1 ELSE #lastSN + 1 END,
toUpdate.SortingNumber = #lastSN
;
Result is:
ID | Parent | SortingNumber
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 4
5 | 2 | 5
6 | 2 | 6
I guess my question could be phrased as: how do I update datasets depending on the Parent column?
PS: here is the CREATE statement if you wish to try it out yourself
CREATE TABLE T
(
ID INT IDENTITY(1,1) PRIMARY KEY,
Parent INT FOREIGN KEY REFERENCES T(ID),
SortingNumber INT
);
GO
INSERT INTO T (Parent, SortingNumber)
VALUES (1, 3), (1, 4), (1, 5), (2, 8), (2, 10), (2, 13);
You can employ row_number to achieve this using partitioning by Parent and ordering by SortingNumber.
WITH cte AS (
SELECT
* ,
ROW_NUMBER() OVER (PARTITION BY Parent ORDER BY SortingNumber) AS NewSortingNumber
FROM T
)
UPDATE cte
SET SortingNumber = NewSortingNumber
A window function creates small tables within the table using Parent, so we have two subsets, one for Parent = 1 and the another for Parent = 2. Then it uses ORDER BY to know from which row it should start count (starting from 1). The first row is for Parent = 1 and ID =1 so it gets 1, the next row gets 2 etc. Please look here for more details.
As an alternative you can just rank, ordering by patient then ID:
UPDATE tt
SET sortingnumber = drank from (select *, DENSE_RANK() OVER (order by Parent, ID) as drank from tt ) a where tt.ID=a.id and tt.parent=a.parent
select * from tt

Limit the rows if same id repeats

I have a table like below
ID | s_id | mark
-----------------------
1 | 2 | 10
2 | 5 | 9
3 | 7 | 8
4 | 2 | 8
5 | 2 | 10
6 | 5 | 7
7 | 3 | 7
8 | 2 | 9
9 | 5 | 8
I need to get SQL query for output like:-
mark column need to be in descending order.
Same s_id should not repeat more than 2 times
if same s_id repeats more than 2 times, ignore the 3rd result
ID | s_id | mark
-----------------------
1 | 2 | 10
2 | 2 | 9
3 | 3 | 7
4 | 5 | 9
5 | 5 | 8
6 | 7 | 8
Assuming you're using SQL Server, you can just use ROW_NUMBER() to assign a row number to each s_id group based on a descending order of the mark column. Then, retain only those records where this row number is 1 or 2.
SELECT
t.ID, t.s_id, t.mark
FROM
(
SELECT ID, s_id, mark, ROW_NUMBER() OVER (PARTITION BY s_id ORDER BY mark DESC) rn
FROM yourTable
) t
WHERE t.rn <= 2
ORDER BY t.s_id;
Note: You'll notice that the record (s_id, mark) = (2, 10) appears twice in my result set. Based on your input data, this is what is generated. If you really intended to also remove duplicate (s_id, mark) pairs, then let us know and a small correction can be added to the query.
Output:
Demo here:
Rextester
try this code.
;WITH cte
AS (
SELECT ROW_NUMBER() OVER (PARTITION BY s_id
ORDER BY ( SELECT 0)) RN,ID,s_id,mark
FROM aaa)
select RN,ID,s_id,mark FROM cte
WHERE RN <= 2
order by s_id,mark desc;

Getting list of spatial points from polygon within query

I have a database with various defined polygons which represent the outer boundarys of buildings on a map of a business park.
If I perform a Select within Management Studio, I get a result similar to the following:
LocationCode LocationPolygon
1 POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))
2 POLYGON((10 10, 20 10, 20 20, 10 20, 10 10))
What I would like to get is the following:
LocationCode PointX PointY
1 1 1
1 2 1
1 2 2
1 1 2
2 10 10
etc etc etc
I cannot see anywhere where I can extract the points from the Polygon using SQL Server from within a SQL Query? I can evidentally take the whole polygon and then do the rest on the client, but I would rather deal in SQL if possible.
Any help appreciated in pointing me in the right direction.
I've answered a similar question before and that time I used a user defined function to extract the points and return a table. Assuming a table Locations defined as: (LocationCode int, LocationPolygon geometry) then the following function:
CREATE FUNCTION dbo.GetPoints()
RETURNS #ret TABLE (LocationCode INT, PointX INT, PointY INT)
AS
BEGIN
DECLARE #max INT
SET #max = (SELECT MAX(LocationPolygon.STNumPoints()) FROM Locations)
;WITH Sequence(Number) AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number + 1
FROM Sequence
WHERE Number < #max
)
INSERT INTO #ret
SELECT
l.LocationCode
,l.LocationPolygon.STPointN(nums.number).STX AS PointX
,l.LocationPolygon.STPointN(nums.number).STY AS PointY
FROM Locations l, Sequence nums
WHERE nums.number <= l.LocationPolygon.STNumPoints()
RETURN
END;
When executed as SELECT DISTINCT * FROM dbo.GetPoints() ORDER BY LocationCode; will give the following result (using your sample data):
| LOCATIONCODE | POINTX | POINTY |
|--------------|--------|--------|
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 2 | 1 |
| 1 | 2 | 2 |
| 2 | 10 | 10 |
| 2 | 10 | 20 |
| 2 | 20 | 10 |
| 2 | 20 | 20 |
I'm sure the function can be improved, but it should give you some ideas on how this problem can be solved.
Sample SQL Fiddle

SqlServer clustered index storage ( >1 columns )?

lets say i have a table like this :
a | b | c | d
______________
1 | 2 | 4 | 5
6 | 2 | 5 | 5
3 | 5 | 2 | 5
[a] column has clustered index
so the physical order which its stored is :
a | b | c | d
______________
1 | 2 | 4 | 5
3 | 5 | 2 | 5
6 | 2 | 5 | 5
now lets enhance the [a] index to be [a,c] ( still as clustered).
now , I can't udnerstand how it can be stored since [a] column is already sorted and [c] column cant be sorted ( because sorting of [a] hurts the sorting of [c])
so how does sqlServer will store it ?
2'nd question : do I need to open another index for [c] ?
I think you're missing something obvious. Consider what you would expect from the query
select * from myTable
order by [a], [c]
Your clustered index on columns [a,c] will give a physical layout with the same order.
Composite indexes produce lexicographical order: the records are additionally ordered on c when values of a are considered "equal".
a c
1 2
2 3 -- Within this block, records are sorted on [c]
2 5 --
2 7 --
3 7
4 1
5 6 -- Within this block, records are sorted on [c]
5 8 --
This is how dictionaries sort.
You need an additional index on c if you want to speed up queries not involving a:
SELECT *
FROM mytable
WHERE c = #some_value

Resources