How to set variable from nth row of SQL query - sql

I am kinda new to SQL and I want to set a variable from a query like this:
DECLARE #value int;
SET #value = (SELECT *
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY ID ASC) AS rownumber
FROM
(SELECT ID FROM Users
WHERE ID NOT IN (SELECT UserID FROM UserFavoritePlaces))
) AS foo
WHERE rownumber = 1)
But I am getting a syntax error
Msg 102, Level 15, State 1, Line 31
Incorrect syntax near ')'
And I have no idea what I am doing wrong.

Your query is overly complicated. You can use this approach as:
SELECT #value = id
FROM (SELECT u.*, ROW_NUMBER() OVER (ORDER BY ID ASC) AS rownumber
FROM Users
WHERE u.ID NOT IN (SELECT ufp.UserID FROM UserFavoritePlaces ufp)
)
WHERE rownumber = 1;
But it might be even simpler as:
SELECT #value = id
FROM Users
WHERE u.ID NOT IN (SELECT ufp.UserID FROM UserFavoritePlaces ufp)
ORDER BY id
OFFSET 0 FETCH FIRST 1 ROW ONLY;
Actually, for your attempted logic, the simplest form is:
SELECT #value = 1;
You are setting the value to the row number. If this is what you intend, then EXIST is usually a better approach.

With a slightly different layout, the cause of the syntax error becomes clear...
Declare #value int;
SET #value=
(
SELECT
*
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID ASC) AS rownumber
FROM
(
SELECT
ID
FROM
Users
WHERE
ID NOT IN (SELECT UserID FROM UserFavoritePlaces)
)
-- NO TABLE ALIAS HERE
)
AS foo
WHERE
rownumber =1
)
Also, your foo table only has the rownumber column. So, even if it executued, it would always return 1.
I would just do...
SELECT
#value = MIN(ID)
FROM
Users
WHERE
ID NOT IN (SELECT UserID FROM UserFavoritePlaces)

Related

SQL update query with multiple subqueries from one table with conditions from the original table

I have the following query to get the latest GPS record for multiple devices from my Records table:
DECLARE #Result TABLE
(DeviceID int, RecordID int, DeviceTime datetime, Own bit, New bit, ...);
--insert a few device IDs into #Result and set New = 0 and Own to 0 or 1
--then:
UPDATE #Result
SET New = 1,
RecordID = (SELECT TOP(1) ID FROM Records WHERE Records.Device_ID = [#Result].DeviceID ORDER BY Records.DeviceTime DESC),
GPSTime = (SELECT TOP(1) DeviceTime FROM Records WHERE Records.Device_ID = [#Result].DeviceID ORDER BY Records.DeviceTime DESC)
WHERE Own = 1;
Is there any way to select ID and DeviceTime from Records with one subquery or in general optimize this query?
You could phrase this an update join, using a CTE:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Device_ID
ORDER BY DeviceTime DESC) rn
FROM Records
)
UPDATE a
SET New = 1,
RecordID = b.ID,
GPSTime = b.DeviceTime
FROM #Result a
INNER JOIN cte b
ON b.Device_ID = a.DeviceID
WHERE a.Own = 1 AND b.rn = 1;

How can I update column randomly in sql?

I have a table and I want to update some columns in this table randomly.
This is my update script
update personnels set
first_name=(select top 1 first_name from personnels order by NEWID()),
I tried this script but select top 1 first_name from personnels order by NEWID() query took too long. How can I update column efficiantly random.
Try generating a Random sequence outside and update using a self-join. Something like this
;WITH CTE
AS
(
SELECT
RN = ROW_NUMBER() OVER(ORDER BY NEW_ID()),
FristName
FROM personnels
),C2
AS
(
SELECT
RN = ROW_NUMBER() OVER(ORDER BY NEW_ID()),
FristName
FROM personnels
)
UPDATE C2
SET
FristName = CTE.FristName
FROM C2
INNER JOIN CTE
ON C2.RN = CTE.RN
I'm not sure what possible purpose you would want this for but try using the rand function
update personnels
set first_name =
(select first_name
from personnels p
where id = (select rand(1 + (RAND() * max(id)))
from personnels p1))
i lost count on the brackets but think this is OK to run
UPDATE table SET val =
( SELECT val FROM table table1 WHERE col =
( SELECT RAND( 1+ ( RAND() * MAX(col)))
FROM table table2
)
)

SQL Server Row_number in ORDER BY CASE

EDITED THE WHOLE TOPIC.
I need to create a view that sort article per type.
If I only have the type : *VALUE -> I need to show this line only.
If I have the type : *VALUE & 2 -> Still showing row accordingly to *VALUE type only.
If I only have the type : 2 -> Showing this one.
I already did somethink like this :
VALUE* is a value that should come from an another table with a Join.
SELECT Id_item ,Name_item , Type_item , Id_type_item FROM ITEM
WHERE Name_item = 'Gillette' AND (Id_Type_item = VALUE* OR Id_Type_item ='10')
ORDER BY CASE
WHEN row_number() OVER(ORDER BY Id_item DESC , Id_Type_Item DESC) <= 1 THEN 0
ELSE 1
END;
But it does that in the case where we've got both row for the types(*VALUE & 10):
Id_item / Name_item / Type_item / Id_Type_Item
1 Gillette 45 30 (*VALUE)
1 Gillette 2 10
So I think that the order by on the Over() could be useful to always sort by *VALUE (which are in reality another column from another table)
I always want to select 1 row of data only ! :)
I'm guessing, that what you want is the "first" row returned from each SELECT? There's no need to use a separate SELECT statement for each variable on the same table, you can use a window function to do so. I believe this is what you might be after.
WITH CTE AS(
--The following assumes table A and B have the same DDL (which begs the question, why are they different tables?)
SELECT *,
ROW_NUMBER() OVER (PARTITION BY var
ORDER BY (SELECT NULL)) AS RN --Replace SELECT(NULL) with your actual ordering criteria
FROM A
WHERE var IN (1,2)
UNION --ALL(?)
SELECT *
ROW_NUMBER() OVER (PARTITION BY var
ORDER BY (SELECT NULL)) AS RN --Replace SELECT(NULL) with your actual ordering criteria
FROM B
WHERE var IN (3))
SELECT *
FROM CTE
WHERE RN = 1;
Here is a possible solution. In this case ROW_NUMBER, RANK and DENSE_RANK would all work. However, ROW_COUNT is not a valid window function in sql server.
DECLARE #A TABLE(ID INT, Value INT)
DECLARE #B TABLE(ID INT,Value INT)
INSERT INTO #A VALUES (1,1),(2,1),(3,2),(4,3),(5,2),(6,1),(7,3)
INSERT INTO #B VALUES (1,1),(2,1),(3,1),(4,2),(5,3),(6,2),(7,1),(8,3)
;WITH D AS
(
SELECT ID,Value FROM #A WHERE Value IN(1,2)
UNION ALL
SELECT ID,Value FROM #B WHERE Value IN (3)
)
SELECT * FROM
(
SELECT
ID, Value,
ValueRankInSet = DENSE_RANK() OVER(PARTITION BY VALUE ORDER BY ID) -- <-- If you do not have an ID field you can subst ID with NEWID() as order is not important
FROM D
)AS X
WHERE ValueRankInSet = 1
Assign the priority within your Select(s) and then order by it in the row_number:
with cte as
(
SELECT *,
row_number()
over (-- partition by ???
order by prio) as Position
FROM
(
SELECT 1 as prio, * FROM A WHERE var = 1
UNION -- probably a more efficient UNION ALL
SELECT 2 as prio, * FROM A WHERE var = 2
UNION -- probably a more efficient UNION ALL
SELECT 3 as prio, * FROM B WHERE var = 3
)
)
select *
from cte
WHERE Position = 1

Inserting by joining on CTE not producing random results

I have created a cte that produces a range of numbers.
What I would like to do is join on this table and do an insert with randomly selected values, this is all to produce test data.
The random portion of the insert would look something like this:
(SELECT TOP 1 Name FROM #Titles ORDER BY NEWID())
The problem is, when I do the insert and join on the CTE my results don't seem random at all? It always uses the same value from #Titles? (Also there are other tables too, like #Notes.
WITH MyCte AS
(SELECT MyCounter = 1
UNION ALL
SELECT MyCounter + 1
FROM MyCte
where MyCounter < 100)
INSERT INTO [MyTable]
( Title, Note )
select (SELECT TOP 1 Name FROM #Titles ORDER BY NEWID()),
(SELECT TOP 1 Note FROM #Notes ORDER BY NEWID())
from mycte
The reason why you're getting the same record 100 times is that SELECT TOP 1 Name FROM #Titles ORDER BY NEWID() is executed only once and then returned for each record in mycte.
You can use a good old loop to achieve what you want. You can use that with multiple tables. It doesn't matter how many records you have in your source tables (though you should have at least 1).
declare #count int = 100
while #count > 0
begin
INSERT INTO [MyTable] ( Title )
SELECT TOP 1 Name FROM #Titles ORDER BY NEWID()
INSERT INTO [MyTable] ( Title )
SELECT TOP 1 Note FROM #Notes ORDER BY NEWID()
set #count = #count - 1
end
As Szymon said in his answer that there is no need to use CTE to get random 100 record from a table. So apart from the answer given by Szymon, you can achieve the same using below query.
INSERT INTO [MyTable] ( Title )
SELECT TOP 1 Name FROM #Titles ORDER BY NEWID()
Go 100
To Specify more than one table.
INSERT INTO [MyTable] ( Title, Note )
SELECT (SELECT TOP 1 Name FROM #Titles ORDER BY NEWID()),
(SELECT TOP 1 Name FROM #Notes ORDER BY NEWID())
GO 100
and now check your result in MyTable table.
SELECT * FROM MyTable

Incrementing int column in MS SQL Server table via SP

Is there a T-SQL statement to auto fill an empty column in a table with incremented values starting at one specific value?
E.g.
UPDATE A SET A.NO = ROW_NUMBER() OVER (ORDER BY A.NO) + #max WHERE A.NO is NULL
This statement doen't work and I don't know how to make it run...
Any help is appreciated!
WITH q AS
(
SELECT a.*, MAX(no) OVER() + ROW_NUMBER() OVER (ORDER BY a.no) AS rn
FROM a
)
UPDATE q
SET no = rn
This works. You need to decouple the ranking function from the update
UPDATE
bar
SET
NO = bar.foo + #max
FROM
(SELECT
A.NO,
ROW_NUMBER() OVER (ORDER BY A.NO) AS foo
FROM
A
WHERE
A.NO is NULL
) bar