Update with Where, Order by and Limit does not work - sql

I am using SQLite 3.
When I input the following query
UPDATE MyTable Set Flag = 1 WHERE ID = 5 Order By OrderID DESC LIMIT 1;
I will always get an error:
near Order By, syntax error
I cannot figure out what is the problem with my query

To use LIMIT and ORDER BY in an UPDATE or DELETE statement, you have to do two things:
Build a custom version of the sqlite3.c amalgamation from source, configuring it with the --enable-update-limit option.
Compile that custom sqlite3.c into your project with SQLITE_ENABLE_UPDATE_DELETE_LIMIT defined to 1.

"Order By OrderID DESC LIMIT 1" is for selecting top one ordered result
so you should use it in select query.
you should do a subquery where you first get the id and then update it:
UPDATE MyTable
SET Flag = 1
WHERE (ID,OrderID) IN (SELECT ID,OrderID
FROM MyTable
WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
Demo

You could use ROWID:
UPDATE MyTable
SET Flag = 1
WHERE ROWID IN (SELECT ROWID FROM MyTable WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
db<>fiddle demo
or (ID,OrderID) tuple:
UPDATE MyTable
SET Flag = 1
WHERE (ID, ORDERID) IN (SELECT ID, ORDERID FROM MyTable WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
db<>fiddle demo2
And if you need to do it in bulk for every ID(SQLite 3.25.0):
WITH cte AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY OrderID DESC) AS rn FROM tab
)
UPDATE tab
SET Flag = 1
WHERE (ID, OrderID) IN (
SELECT ID, OrderID
FROM cte
WHERE rn = 1
);

Order By statement will not work in update query.
You have to use alternate way
UPDATE MyTable Set Flag = 1 WHERE ID = 5
and OrderId = (select max(OrderId) from MyTable where Id = 5);
If you have used the query like above it will work.

Introduction
Below I'm presenting two solutions to solve the issue and perform a proper UPDATE. At the end of each solution there's a live example on sample data included.
First does not require you to input any id and works for the entire table by picking the latest orderid for each id and changing it's flag to 1
Second requires you to input an id and works only for updating one id on the run
I'd personally go with first solution, but I am not sure about your requirement, thus posting two possibilities.
First solution - update entire table
Explanation here, for code scroll down.
For this we will use Row Value construction (id, orderid) just like for the second solution.
It will find the latest row based on orderid and update only that row for given (id, orderid) pair. More on that is included in Explanation of second solution.
We will also need to simulate row_number function to assign each row ranking numbers, to find out which row has the latest orderid for every id and mark then as 1 to be able to pull only those for update. This will allow us to update multiple rows for different ids in one statement. SQLite will have this functionality built in version 3.2.5 but for now, we will work with a subquery.
To generate row numbers we will use this:
select
*,
(select count(*) from mytable m1 where m1.id = m2.id and m1.orderid >= m2.orderid) as rn
from mytable m2
Then we just need to filter the output on rn = 1 and we have what we need.
That said, the whole UPDATE statement will look like:
Code
update mytable
set flag = 1
where (id,orderid) in (
select id, orderid
from (
select *, (select count(*) from mytable m1 where m1.id = m2.id and m1.orderid >= m2.orderid) as rn
from mytable m2
) m
where
m.rn = 1
and m.id = mytable.id
);
Live DEMO
Here's db fiddle to see this solution live on sample data.
Second solution - update only one ID
If you know your ID to be updated and want to run UPDATE statement for only one id, then this will work:
Code
update mytable
set flag = 1
where (id,orderid) in (
select id, orderid
from mytable
where id = 5
order by orderid desc
limit 1
);
Explanation
(id, orderid) is a construction called Row Value for which SQLite compares scalar values from left to right.
Example taken from documentation:
SELECT (1,2,3) = (1,2,3) -- outputs: 1
Live DEMO
Here's db fiddle to see this solution live on sample data.

SQL
UPDATE MyTable
SET Flag = 1
WHERE ID = 5
AND OrderID IN (SELECT OrderID
FROM MyTable
WHERE ID = 5
ORDER BY OrderID DESC
LIMIT 1);
Demo
SQLFiddle demo: http://sqlfiddle.com/#!5/6d596/2

Related

Update table based on the condition

I need to update the staging table based on the type if ZMD2 is present then update the records else update PNTP records.
UPDATE ITEMS_STAGING SET TYPE=b.TYPE,VALUE=b.VALUE
FROM ITEMS_STAGING a,ITEMS b
WHERE a.PARENT=b.PARENT
In the above statement I need to pick only ZMD2 records for the same parent if exists if not PNTP records. I tried to do UNION for the ITEMS it dint help.
Staging table Output:
Kindly help.
Thanks
You need to use analytical function row_number which will group the rows by parent column to give them numbers and then we will take only one record from each group to update staging table using merge statement as following:
MERGE INTO ITEM_STAGING M
USING (
SELECT T.*,
ROW_NUMBER() OVER(PARTITION BY T.PARENT ORDER BY T.TYPE DESC) RN
FROM ITEMS T
)
ON (M.PARENT = T.PARENT AND T.RN = 1)
WHEN MATCHED THEN
UPDATE SET M.TYPE = T.TYPE AND M.VALUE = T.VALUE;
Cheers!!
You may try below query -
SELECT *
FROM (SELECT IS.*, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY TYPE DESC) RN
FROM ITEMS_STAGING)
WHERE RN = 1;
I am not sure what you want to update in this table.

LIMIT equivalent for SQL Server 2012 in an UPDATE statement

I have the following table below and am trying to update the first available row with an user ID through a query, but I need to limit this to only update one row and not multiple.
ID Model UserID
1 X12T5 1
2 X13T5 2
3 X14T5 NULL
4 X15T5 NULL
The first available row would be where ID is 3. I would update it with the following query:
UPDATE Table SET UserID = '3' WHERE UserID IS NULL
But I want to make sure it affects only 1 row and not multiple that are available, LIMIT doesn't exist in SQL Server.
What would the best way to achieve this?
You can do this with UPDATE TOP. It's the equivalent of a SELECT TOP but for updates; and TOP is SQL Server's equivalent of MySQL's LIMIT.
See further info.
UPDATE Table SET UserID = '3'
WHERE UserID IS NULL
AND Id IN (SELECT top 1 ID FROM table where UserId IS NULL)
You can also use any of the following methods (Assuming column ID is of integer datatype)
Using subquery :
UPDATE a
SET a.UserID='3'
FROM YourTable a
WHERE ID =( SELECT MIN(ID)
FROM YourTable
WHERE UserID is NULL)
Using JOIN:
UPDATE a
SET a.UserID='3'
FROM YourTable a
JOIN
( SELECT MIN(ID) MinId
FROM YourTable
WHERE UserID is NULL) b
ON a.ID=b.MinId
Using CTE
WITH cte_a
AS
(SELECT MIN(ID) MinId
FROM YourTable
WHERE UserID is NULL)
UPDATE a
SET a.UserID='3'
FROM YourTable a
JOIN cte_a b a.ID=b.MinId
You can also use CTE & perform the SELECT\UPDATE\DELETE operations ,
; WITH CTE AS (
SELECT TOP 10 * FROM TABLE
)
UPDATE CTE
SET ...
DELETE\UPDATE\SELECT works with CTE ... Cool !!!

Select a NON-DISTINCT column in a query that return distincts rows

The following query returns the results that I need but I have to add the ID of the row to then update it. If I add the ID directly in the select statement it will return me more results then I need because each ID is unique so the DISTINCT statement see the line as unique.
SELECT DISTINCT ucpse.MemberID, ucpse.ProductID, ucpse.UserID
FROM UserCustomerProductSalaryExceptions as ucpse
WHERE EXISTS (SELECT NULL
FROM UserCustomerProductSalaryExceptions as upcse2
WHERE ucpse.userid = upcse2.userid AND ucpse.MemberID = upcse2.MemberID AND ucpse.ProductID = upcse2.ProductID
GROUP BY upcse2.UserID, upcse2.memberid, upcse2.productid
HAVING COUNT(UserID) >= 2
)
So basically I need to add ucpse.ID in the Select statement while keeping DISTINCT values for MemberID,ProductID and UserID.
Any Ideas ?
Thank you
According to you comment:
If the data has been duplicated 67 times for a given employee with a given product and a given client, I need to keep only one of thoses records. It's not important which one, so this is why I use DISTINC to obtain unique combinaison of given employee with a given product and a given client.
You can use MIN() or MAX() and GROUP BY instead of DISTINCT
SELECT MAX(ucpse.ID) AS ID, ucpse.MemberID, ucpse.ProductID, ucpse.UserID
FROM UserCustomerProductSalaryExceptions as ucpse
WHERE EXISTS (SELECT NULL
FROM UserCustomerProductSalaryExceptions as upcse2
WHERE ucpse.userid = upcse2.userid AND ucpse.MemberID = upcse2.MemberID AND ucpse.ProductID = upcse2.ProductID
GROUP BY upcse2.UserID, upcse2.memberid, upcse2.productid
HAVING COUNT(UserID) >= 2
)
GROUP BY ucpse.MemberID, ucpse.ProductID, ucpse.UserID
UPDATE:
From you comments I think the below query is what you need
DELETE FROM UserCustomerProductSalaryExceptions
WHERE ID NOT IN ( SELECT MAX(ucpse.ID) AS ID
FROM #UserCustomerProductSalaryExceptions
GROUP BY ucpse.MemberID, ucpse.ProductID, ucpse.UserID
HAVING COUNT(ucpse.ID) >= 2
)
If all you want is to delete the duplicates, this will do it:
WITH X AS
(SELECT ID,
ROW_NUMBER() OVER (PARTITION BY MemberID, ProductID, UserID ORDER BY ID) AS DupRowNum<br
FROM UserCustomerProductSalaryExceptions
)
DELETE X WHERE DupRowNum > 1
ID's not necessary - try:
UPDATE uu SET
<your settings here>
FROM UserCustomerProductSalaryExceptions uu
JOIN ( <paste your entire query above here>
) uc ON uc.MemberID=uu.MemberId AND uc.ProductID=uu.ProductId AND uc.UserID=uu.UserId
From the sound of your data structure (which I would STRONGLY advise normalizing as soon as possible), it sounds like you should be updating all the records. It sounds as if each duplicate is important because it contains some information about an employee's relation to a customer or product.
I would probably update all the records. Try this:
UPDATE UCPSE
SET
--Do your updates here
FROM UserCustomerProductSalaryExceptions as ucpse
JOIN
(
SELECT UserID, MemberID, ProductID
FROM UserCustomerProductSalaryExceptions
GROUP BY UserID, MemberID, ProductID
HAVING COUNT(UserID) >= 2
) T
ON ucpse.UserID = T.UserID AND ucpse.MemberID = T.MemberID AND ucpse.ProductID = T.ProductID

How to update a column of a table to a scaling value

I'm trying to update a column in my table to use the values 1 through (a max number decided by a count of records).
I don't know if I'm explaining this right, so I set up a SQLFiddle with the data I'm trying to update.
SQL FIDDLE
I want to set the Version column to 1 through (the max number).
Is there some way to rewrite this query to a scale the Version number?
As in, I want the first record to use 1, the second record to use 2, and so on...
UPDATE Documents
SET Version = 1
You can do it with a CTE and no joins:
with RankedDocument as
(
select *
, rn = row_number() over (order by ID)
from Documents
)
update RankedDocument
set Version = rn
SQL Fiddle with demo.
From what I can tell, you want every record from Documents to have a version number which is a number moving from 1 ..... N.
You could use a temporary table and ROW_NUMBER technique to get the incremental version and then UPDATE it back to your original table.
CREATE TABLE #Temp (ID int, Version int)
INSERT INTO #Temp (ID, Version)
SELECT ID, ROW_NUMBER() OVER (ORDER BY ID ASC)
FROM Documents
UPDATE Doc
SET Version = TT.Version
FROM Documents AS Doc INNER JOIN #Temp AS TT ON Doc.ID = TT.ID
DROP TABLE #Temp
If I understand you correctly..
Try this:
;WITH list AS (
SELECT
ID
, Version = ROW_NUMBER() OVER( ORDER BY VersionID ASC )
FROM Documents
)
UPDATE d SET
d.Version = x.Version
FROM Documents AS d
INNER JOIN list as x ON d.ID=x.ID
SELECT * FROM Documents
You can change the order ( ORDER BY VersionID ASC )
to the one you need.

Order by clause is changing my result set

I know why it's happening but I want to find a way around it if possible.
For example I have 4 rows in my database and each has a datetime (which are all different). What I want to do is get the latest 2 rows but use ascending order, so that the oldest is at the top of the result set.
I currently am using
SELECT TOP 2 *
FROM mytable
WHERE someid = #something
ORDER BY added DESC
This gets me the correct rows but in the wrong order. If I change the DESC to ASC it gets the right order, but the older two of the four rows. This all makes sense to me but is there a way around it?
EDIT: Solved with Elliot's answer below. The syntax would not work without setting an alias for the derived table however. Here is the result
SELECT * FROM
(SELECT TOP 2 * FROM mytable WHERE someid = #something ORDER BY added DESC) AS tbl
ORDER BY tbl.added ASC
I'd think one brute-force solution would be:
SELECT *
FROM (SELECT TOP 2 * FROM mytable WHERE someid = #something ORDER BY added DESC)
ORDER BY added
This will allow "top 2 per something" with a PARTITION BY added to the OVER clause
SELECT *
FROM
(
SELECT *, ROW_NUMBER() OVER (ORDER BY added DESC) as rn
FROM mytable
WHERE someid = #something
) foo
WHERE rn <= 2
ORDER BY added
Note that the derived table requires an alias