Get last but one row for each ID - mysql

I am using query like
select * from audittable where a_id IN (1,2,3,4,5,6,7,8);
For each ID its returning 5-6 records. I wanted to get the last but one record for each ID.
Can i do this in one sql statement.

Try this query
SELECT
*
FROM
(SELECT
#rn:=if(#prv=a_id, #rn+1, 1) as rId,
#prv:=a_id as a_id,
---Remaining columns
FROM
audittable
JOIN
(SELECT #rn:=0, #prv:=0) t
WHERE
a_id IN (1,2,3,4,5,6,7,8)
ORDER BY
a_id, <column> desc)tmp --Replace column with the column with which you will determine it is the last record
WHERE
rId=1;

If your database is having DateCreated or any column in which you are saving the DateTime as well like when your data is inserted for a particular row then you may use query like
select at1.* from audittable at1 where
datecreated in( select max(datecreated) from audittable at2
where
at1.id = at2.id
order by datecreated desc
);
You may also use LIMIT function as well.
Hope you understand and works for you.

In SQLite, you have the columns a_id and b. For each a_id you get a set of b's. Let you want
to get the latest/highest (maximum in terms of row_id, date or another naturally increasing index) one of b's
SELECT MAX(b), *
FROM audittable
GROUP BY a_id
Here MAX help to get the maximum b from each group.
Bad news that MySQL doesn't associate MAX b with other *-columns of the table. But it still can be used in case of simple table with a_id and b columns!

Related

Delete a MySQL selection

I would like to delete my MySQL selection.
Here is my MySQL selection request:
SELECT *
FROM Items
WHERE id_user=1
ORDER
BY id_user
LIMIT 2,1
With this working request, I select the third item on my table which has as id_user: 1.
Now, I would like to delete the item that has been selected by my request.
I am looking for a same meaning request which would look like this :
DELETE FROM Items (
SELECT * FROM Items WHERE id_user=1 ORDER BY id_user LIMIT 2,1
)
The first thing to note is that there is an issue with your query. You are filtering on a unique value of id_user and sorting on the same column. As all records in the resultset will have the same id_user, the actual order of the resultset is undefined, and we cannot reliably tell which record comes third.
Assuming that you have another column to disanbiguate the resultset (ie some value that is unique amongst each group of records having the same id_user), say id, here is a solution to your question, that uses a self-join with ROW_NUMBER() to locate the third record in each group.
DELETE i
FROM items i
INNER JOIN (
SELECT
id,
id_user,
ROW_NUMBER() OVER(PARTITION BY id_user ORDER BY id) rn
FROM items
) c ON c.id = i.id AND c.id_user = i.id_user AND c.rn = 3
WHERE i.id_user=1 ;
Demo on DB Fiddle
You didn't provide the definition of your table. I guess it has a primary key column called id.
In that case you can use this
CREATE TEMPORARY TABLE doomed_ids
SELECT id FROM Items WHERE id_user = 1 ORDER BY id_user LIMIT 2,1;
DELETE FROM Items
WHERE id IN ( SELECT id FROM doomed_ids);
DROP TABLE doomed_ids;
It's a pain in the neck, but it works around the limitation of MySQL and MariaDB disallowing LIMITs in ... IN (SELECT ...) clauses.
You can use the select query to create a derived table and join it back to your main table to determine which record(s) to delete. Derived tables can use the limit clause.
Assuming that the PK is called id, the query would look as follows:
delete i from items i
inner join (SELECT id FROM Items
WHERE id_user=1
ORDER BY id_user LIMIT 2,1) i2 on i.id=i2.id
You need to substitute your PK in place of id. If you have a multi-column PK, then you need to select all the PK fields in the derived table and join on all of them.

SQL find distinct and show other columns

I have read many replies and to similar questions but cannot seem to apply it to my situation. I have a table that averages 10,000 records and is ever changing. It containing a column called deviceID which has about 20 unique values, another called dateAndTime and many others including status1 and status2. I need to isolate one instance each deviceID, showing the record that had the most current dateAndTime. This works great using:
select DISTINCT deviceID, MAX(dateAndTime)
from MyTable
Group By deviceID
ORDER BY MAX(dateAndTime) DESC
(I have noticed omitting DISTINCT from the above statement also yields the same result)
However, I cannot expand this statement to include the fields status fields without incurring errors in the statement or incorrect results. I have tried using IN and EXISTS and syntax to isolate rows, all without luck. I am wondering how I can nest or re-write this query so that the results will display the unique deviceID's, the date of the most recent record and the corresponding status fields associated with those unique records.
If you can guarantee that the DeviceID + DateAndTime is UNIQUE you can do the following:
SELECT *
FROM
MyTable as T1,
(SELECT DeviceID, max(DateAndTime) as mx FROM MyTable group by DeviceID) as T2
WHERE
T1.DeviceID = T2.DeviceID AND
T1.DateAndTime = T2.mx
So basically what happens is, that you do a group by on the DeviceID (NOTE: A GROUP BY always goes with an aggregate function. We are using MAX in this case).
Then you join the Query with the Table, and add the DeviceID + DateAndTime in the WHERE clause.
Side Note... GROUP BY will return distinct elements with or without adding DISTINCT because all rows are distinct by default.
Maybe:
SELECT a.*
FROM( SELECT DISTINCT *,
ROW_NUMBER() OVER (PARTITION BY deviceID ORDER BY dateAndTime DESC) as rown
FROM MyTable ) a
WHERE a.rown = 1

select A, B , C group by B with A from the row that has the highest C

I have collected informations from different sources about certain IDs that should match a single name. Some sources are more trustworthy than others in giving the correct name for a given ID.
I created a table (name, id, source_trustworthiness) and I want to get the most trustworthy name for each ID.
I tried
SELECT name, id, MAX( source_trustworthiness )
FROM table
GROUP BY id
this returns th highest trustworthiness available for each ID but with the first name it finds, regarless of its trustworthiness.
Is there a way I can get that right ?
Mysql has special functionality to help:
SELECT * FROM (
SELECT name, id, source_trustworthiness
FROM table
ORDER BY 3 DESC ) x
GROUP BY id
Although this wouldn't even execute in other databases (not naming all non-aggregate columns in the GROUP BY clause), with mysql it returns the first row encountered for each unique value of the grouped by columns. By ordering the rows greatest first, the first row for each id will be the most trustworthy.
Since this question is tagged mysql, this query is OK. Not only is it really simple, it's also quite fast.
SELECT a.*
FROM TableName a
INNER JOIN
(
SELECT id, MAX(source_trustworthiness) max_val
FROM TableName
GROUP BY ID
) b ON a.ID = b.ID AND
a.source_trustworthiness = b.max_val

Copy rows if value exists x amount of times

I have two tables Board1 and Board2 with the identical structure. They both have a primary index column of id. I have a THIRD table called Table1, which has a non-indexed column board_id, where the same board_id occurs multiple times. board_id always corresponds to an id in Board1. Board2 is currently empty, and I want to add rows from Board1, but only where the same board_id occurs at least six times in Table1. Table1 will be changing periodically, so I'll be needing to do the query in the future, but without doubling id rows which are already in Board2.
So to recap:
There are three tables: Board1, Board2, and Table1. I want to copy rows from Board1 to Board2, but only where the id in the Board1 occurs (at least) six times in Table1 as `board_id'.
I'd appreciate any help!
EDIT: I'm dreadfully sorry, but I realized I made a huge mistake in my question. I've rewritten it to reflect what I actually needed. I'm truly sorry.
You can do it like this
INSERT INTO Table2
SELECT
id,
board_id
FROM (SELECT
b.id,
b.board_id,
bl.Count
FROM board as b
LEFT JOIN (SELECT
board_id,
COUNT(board_id) as `Count`
FROM board
GROUP BY board_id) as bl
on bl.board_id = b.board_id
group by b.id
having bl.Count >= 6) as L
If you need more columns you can select them in inner and outer queries.
Fiddle Demo for Select
Here is what you asked for, with fiddle
INSERT Table2
SELECT
*
FROM
Table1
JOIN
(
SELECT
Board_Id,
count(*) cnt
FROM
Table1
GROUP BY
Board_Id
) BoardIds
ON BoardIds.Board_Id = Table1.Board_Id
WHERE
BoardIds.cnt > 5
AND
NOT EXISTS (SELECT id FROM Table2 WHERE Table2.id = Table1.id)
Try something like the below:
Add your column names where specified (excluding any ID columns), as I'm assuming each row will have a unique ID, so you won't be able to GROUP and COUNT by doing SELECT * FROM Table1
You may need to test / validate this
INSERT INTO Board2 (Your Column Names)
SELECT (Your Column Names)
FROM Board1
WHERE id (IN (SELECT board_id
FROM Table1
GROUP BY (board_id)
HAVING (COUNT(*) >= 6))
AND board_id NOT IN(SELECT DISTINCT board_id FROM Board2)

MySQL -- Making it all one query

I am trying to see if I can accomplish the following situation in one query:
I have a table with multiple columns, however, only two are important: version and groupId.
Many rows can share the same groupId value, the version column is a number that needs
to be sorted.
Given two groupId values, A and B, I would like to return two rows in the end. I want to find the most recent version number for each group A and B.
Thanks for your help. Sorry if this is fairly obvious, but I was having difficulty
Something like this?
SELECT groupId, MAX(version) max_version
FROM YourTable
WHERE groupId IN ('A', 'B')
GROUP BY groupId;
You didn't specify any data types so I assumed that groupId could actually take character values like 'A'. Just change this to suit your needs. The basic idea is that you GROUP BY your groupId after filtering out only those values which interest you. Then you SELECT the MAX(version) for each of those values.
Try below
select p.id,p.groupid,p.version from tablename p
left join
(
select max(id) id1 from tablename
group by groupId
order by max(id) desc
) t on t.id1 = p.id
Assuming you have a primary key column id in table
I assume version is an integer
SELECT MAX(version), `group` FROM table WHERE `group` IN (A, B) GROUP BY `group`