If conditions in SQL - sql

Clarification. I want to have a query that finds me the row with newest Timestamp, but if that row has column deleted set to 1 then I want to get back NULL. That is what I am trying to do.
I would like to make a query with sub query in WHERE clause something like:
SELECT *
FROM table
WHERE id = (SELECT id FROM table WHERE ts > 'x' ORDER BY ts LIMIT 1)
But the thing is there is a column called DELETED in table, and if DELETED = 1 I would like to get just this
SELECT *
FROM table
WHERE id = NULL
How could I do that?
I am working with MariaDB, but any SQL would help.

I understand that you want the "DELETED" result if there is any. Else you want the row with a ts greater than x. Assuming that "DELETED is 0 if not 1, you can use Gordons SQL like this:
SELECT t.*
FROM table t
WHERE (t.ts > 'x' or t.deleted = 1)
ORDER BY t.deleted desc, t.ts
LIMIT 1;

Check this one:
SELECT *
FROM table
WHERE id = (SELECT id FROM table WHERE ts > 'x' ORDER BY ts LIMIT 1)
AND deleted = 0;

I don't think this is related to your question, but why not just execute the query as:
SELECT t.*
FROM table t
WHERE t.ts > 'x'
ORDER BY t.ts
LIMIT 1;
It seems simpler than your version of the query -- assuming that id is unique in the table.

Related

Update with Where, Order by and Limit does not work

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

Oracle SQL update every nth row

To do some testing on a new table field, I'd like to fake-up some values on existing records in my test database. I want to assign a value to every 8th record in a table. I can easily select every 8th record using this syntax:
select *
from
(select rownum rn
, jeffs_field_to_update
from jeff)
where mod(rn, 8) = 0;
However, I'm fairly new to SQL, and I can't seem to be able to convert this to an update statement. I see a lot of answers here about selecting nth records, but I've already got that. Any assistance would be appreciated.
You need to join this to UPDATE statement on any key in the table. For example, if you have an unique id column, update statement will look like this:
update jeff
set jeffs_field_to_update = value
where id in
(select id
from
(select rownum rn
, jeff.id
from jeff)
where mod(rn, 8) = 0)
A shorter answer:
UPDATE jeff
SET jeffs_field_to_update = value
WHERE mod(DBMS_ROWID.ROWID_ROW_NUMBER(ROWID), 8)=0;
Assuming your table has a unique id to identify each row, you can do something like this:
update jeff
set . . .
where id in (select id
from (select rownum as rn, id
from jeff
)
where mod(rn, 8) = 0
)
You should also be able to do this with an inline updatable view (see here):
update (select jeffs_field_to_update
from (select rownum rn, jeffs_field_to_update
from jeff)
where mod(rn, 8) = 0
) toupdate
set . . .
even without the key, you can use rowid in oracle for this purpose.
update mytable
set mycol = new_value
where rowid in
(select rowid from
(select rownum rn, id from mytable)
where mod(rn, 8) = 0)

Select records older than latest 100 records from database

My issue here is that i want to populate all the records except latest 100 records in the database. What could be the best approach to accomplish this?
Try using limit cause
SELECT *
FROM Table
order by id desc
limit 101 , totalrecords
Here id is auto increment field of your table
How about something like
SELECT t.*
FROM Table t LEFT JOIN
(
SELECT ID
FROM Table
ORDER BY ID DESC
LIMIT 100
) top100 ON t.ID = top100.ID
WHERE top100.ID IS NULL
Where ID would be the column to identify the order (latest) and Table from where you wish to select
SELECT *
FROM Table
WHERE ID < (SELECT ID
FROM Table
ORDER BY ID DESC
LIMIT 99, 1)

What is a efficient way to delete the most recent X record from SQL?

I have a simple table where one of the field is a date column. I can select the most recent X records by doing
Select * from MyTable order by last_update desc limit X
But how do I efficiently delete those column? Is subselect the quickest?
If I recall correctly the IN clause accepts a sub select. Could be wrong.
DELETE FROM Person
WHERE
Person.ID IN (
SELECT t.ID
FROM
Person t
ORDER BY
t.joinTime DESC
LIMIT X
)
You can do "delete from where" in exactly the same way, with the same conditions as the original query.
e.g:
DELETE FROM Person WHERE Person.name = "jeff"
or
DELETE FROM Person WHERE Person.joinTime > 12001234567 LIMIT 100
If you want to delete a range (e.g most recent 10) you could try:
DELETE * FROM Person WHERE id >= ( LAST_INSERT_ID() - 10 );
Perhaps something like:
DELETE FROM MyTable WHERE rowid IN
(SELECT rowid FROM MyTable ORDER BY last_update DESC LIMIT X);
DELETE d.*
FROM mytable d
LEFT JOIN
(
SELECT id
FROM mytable
ORDER BY
last_update DESC
LIMIT 10
) q
ON d.id = q.id
WHERE q.id IS NULL
If your are using MySQL, this is the preferred solution, since IN (SELECT ... LIMIT) does not work in MySQL.
See this entry in my blog for more details:
Keeping rows

selecting subsequent records arbitrarily with limit

I want to do a query to retrieve the record immediately after a record for any given record, in a result set ordered by list. I do not understand how to make use of the limit keyword in sql syntax to do this.
I can use WHERE primarykey = number, but how will limiting the result help when I will only have one result?
How would I obtain the next record with an arbitrary primary key number?
I have an arbitrary primary key, and want to select the next one ordered by date.
This will emulate the LEAD() analytic function (i. e. select the next value for each row from the table)
SELECT mo.id, mo.date,
mi.id AS next_id, mi.date AS next_date
FROM (
SELECT mn.id, mn.date,
(
SELECT id
FROM mytable mp
WHERE (mp.date, mp.id) > (mn.date, mn.id)
ORDER BY
mp.date, mp.id
LIMIT 1
) AS nid
FROM mytable mn
ORDER BY
date
) mo,
mytable mi
WHERE mi.id = mo.nid
If you just want to select next row for a given ID, you may use:
SELECT *
FROM mytable
WHERE (date, id) >
(
SELECT date, id
FROM mytable
WHERE id = #myid
)
ORDER BY
date, id
LIMIT 1
This will work most efficiently if you have an index on (date, id)
How about something like this, if you're looking for the one after 34
SELECT * FROM mytable WHERE primaryKey > 34 ORDER BY primaryKey LIMIT 1
Might be as simple as:
select *
from mytable
where datecolumn > (select datecolumn from mytable where id = #id)
order by datecolumn
limit 1
(Edited after comments)