MySQL NOT IN Select - mysql

I have a following table in my project
+----+--------+-----------+-----------+
| id | old_id | op_status | tr_status |
+----+--------+-----------+-----------+
| 1 | | issue | Approved |
| 2 | | issue | Approved |
| 3 | | issue | Approved |
| 4 | 1 | issue | Issued |
| 5 | 3 | issue | Issued |
+----+--------+-----------+-----------+
I want to select records WHERE tr_status='Approved' and NOT IN id in the old_row_id. In this example no need to select id(s) 1 and 3 that are in old_row_id as the following result.
+----+--------+-----------+-----------+
| id | old_id | op_status | tr_status |
+----+--------+-----------+-----------+
| 2 | | issue | Approved |
+----+--------+-----------+-----------+
I used the following query.
SELECT id, old_row_id, op_status, tr_status FROM table WHERE id NOT IN (old_row_id).
But outs the following result.
+----+--------+-----------+-----------+
| id | old_id | op_status | tr_status |
+----+--------+-----------+-----------+
| 1 | | issue | Approved |
| 2 | | issue | Approved |
| 3 | | issue | Approved |
+----+--------+-----------+-----------+
What may be wrong with me ? can anyone help me ?

I would phrase your query using exists logic:
SELECT t1.id, t1.old_id, t1.op_status, t1.tr_status
FROM yourTable t1
WHERE
t1.tr_status = 'Approved' AND
NOT EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.old_id = t1.id);
Demo

Try doing something like this:
SELECT id, old_id, op_status, tr_status
FROM table
WHERE id NOT IN (SELECT DISTINCT old_id FROM table)
AND tr_status = 'Approved'

You will want to left join the table to itself on the old ID, then eliminate the records where there is a match.
For example:
SELECT A.id, A.old_row_id, A.op_status, A.tr_status
FROM table A
LEFT JOIN table B ON A.id = B.old_row_id
WHERE B.id IS NULL
AND A.tr_status = 'Approved';

In they way you've tried to solve it, you're matching the old_id with the value of the row itself. You must derivate the table to create a cartesian product:
SELECT id, old_id, op_status, tr_status
FROM table
WHERE id NOT IN (SELECT IFNULL(old_id, 0) FROM table)
AND tr_status = 'Approved'
Also IFNULL to also include those record in the subquery, otherwise can't be compared with null

Related

MySQL: Multi Tables - One to many relationship, Get column whose one of child's column is not null

I need help in creating sql query.
I want to get all orders whose none of child's res_id is null.
In the below example you will see order_audit.order_id W1 have one to
many relationship temp_order_id W1_1 and W1_2. This temp_order_id has further res_id 12 and 32. This order W1 should be in response.
In case of W2 you can see W2_1 has resp_id null. So this should not be pulled.
order_audit
+----+----------+
| id | order_id |
+----+----------+
| 1 | W1 |
| 2 | W2 |
| 2 | W3 |
+----+----------+
order_mapping
+----------+---------------+
| order_id | temp_order_id |
+----------+---------------+
| W1 | W1_1 |
| W1 | W1_2 |
| W2 | W2_1 |
| W2 | W2_2 |
| W3 | W3_1 |
+----------+---------------+
temp_order_table
+---------------+--------+
| temp_order_id | res_id |
+---------------+--------+
| W1_1 | 12 |
| W1_2 | 32 |
| W2_1 | null |
| W2_2 | 33 |
| W3_1 | null |
+---------------+--------+
From you screenshot it looks like there is a leading space in Account (and maybe there are trailing ones as well).
Any kind of help would be appreciated
You can natural join all 2 other tables and check for if res_id is null.
select oa.id, oa.order_id from order_audit oa
where not exists (
select * from order_mapping om
join temp_order_table tot on
tot.temp_order_id = om.temp_order_id
where om.order_id = oa.order_id and tot.res_id is null
)
Here is the link for sqlfiddle link
You could use a NOT IN forn the order with null
select oa.order_id
from order_audit oa
where oa.order_id NOT IN (
select om.order_id
from order_mapping om
inner join (
select to.temp_order_id
from temp_order_table to
where to.res_id is null
) t on t.temp_order_id = om.temp_order_id
)
I would approach this using group by and having:
select om.order_id
from order_mapping om left join
temp_order_table tot
on om.temp_order_id = tot.temp_order_id
group by om.order_id
having count(tot.temp_order_id) = count(tot.res_id);

Mysql delete duplicate record but keep the row which contain most value

if I have following table :
+------+--------+---------+-------+-------+
| id | name | Surnmae | email |address|
+------+--------+---------+-------+-------+
| 1 | | Lee | aaa |23 a st|
| 2 | a | | aaa | |
| 3 | c | | ccc | |
+------+--------+---------+-------+-------+
How can I delete duplicate record base on email but keep the record which contains most value and give the following result?
+------+--------+---------+-------+-------+
| id | name | Surnmae | email |address|
+------+--------+---------+-------+-------+
| 1 | | Lee | aaa |23 a st|
| 3 | c | | ccc | |
+------+--------+---------+-------+-------+
I got a way, but it is kind ok stupid, it works for small table just like the one I provided above:
DELETE FROM table1
WHERE firstname NOT IN (SELECT *
FROM (SELECT MAX(n.firstname)
FROM table1 n
GROUP BY n.email) x)
OR lastname NOT IN (SELECT *
FROM (SELECT MAX(n.lastname)
FROM table1 n
GROUP BY n.email) x)
OR address NOT IN (SELECT *
FROM (SELECT MAX(n.address)
FROM table1 n
GROUP BY n.email) x)
but does anyone has an idea which can simplif my query?? like one line query?
Please try this i assume ur table name is members
delete t1 from members LEFT JOIN members t1 ON members.email = t1.email AND members.id != t1.id AND IFNULL(CHAR_LENGTH(members.Surnmae ),0) > IFNULL(CHAR_LENGTH(t1.Surnmae ),0) WHERE t1.id > 0;

Select the most current records from multiple identical rows in the MySQL database

I am working on a product sample inventory system where I track the movement of the products. The status of each product can have a status of "IN" or "OUT" or "REMOVED". Each row of the table represents a new entry, where ID, status and date are unique. Each product also has a serial number.
I need help with a SQL query that will return all products that are currently "OUT". If I simply just select SELECT * FROM table WHERE status = "IN", it will return all products that ever had status IN.
Every time product comes in and out, I duplicate the last row of that specific product and change the status and update the date and it will get a new ID automatically.
Here is the table that I have:
id | serial_number | product | color | date | status
------------------------------------------------------------
1 | K0T4N | XYZ | silver | 2016-07-01 | IN
2 | X56Z7 | ABC | silver | 2016-07-01 | IN
3 | 96T4F | PQR | silver | 2016-07-01 | IN
4 | K0T4N | XYZ | silver | 2016-07-02 | OUT
5 | 96T4F | PQR | silver | 2016-07-03 | OUT
6 | F0P22 | DEF | silver | 2016-07-04 | OUT
7 | X56Z7 | ABC | silver | 2016-07-05 | OUT
8 | F0P22 | DEF | silver | 2016-07-06 | IN
9 | K0T4N | XYZ | silver | 2016-07-07 | IN
10 | X56Z7 | ABC | silver | 2016-07-08 | IN
11 | X56Z7 | ABC | silver | 2016-07-09 | REMOVED
12 | K0T4N | XYZ | silver | 2016-07-10 | OUT
13 | 96T4F | PQR | silver | 2016-07-11 | IN
14 | F0P22 | DEF | silver | 2016-07-12 | OUT
This query will give you all the latest records for each serial_number
SELECT a.* FROM your_table a
LEFT JOIN your_table b ON a.serial_number = b.serial_number AND a.id < b.id
WHERE b.serial_number IS NULL
Below query will give your expected result
SELECT a.* FROM your_table a
LEFT JOIN your_table b ON a.serial_number = b.serial_number AND a.id < b.id
WHERE b.serial_number IS NULL AND a.status LIKE 'OUT'
There are two good ways to do this. Which way is best,in terms of performance, can depend on various factors, so try both.
SELECT
t1.*
FROM table t
LEFT OUTER JOIN table later_t
ON later_t.serial_number = t.serial_number
AND later_t.date > t.date
WHERE later_t.id IS NULL
AND t.status = "OUT"
Which column you check from later_t for IS NULL does not matter, so long as that column is declared NOT NULL in the table definition.
The other logically equivalent method is:
SELECT
t.*
FROM table t
INNER JOIN (
SELECT
serial_number,
MAX(date) AS date
FROM table
GROUP BY serial_number
) latest_t
ON later_t.serial_number = t.serial_number
AND latest_t.date = t.date
WHERE t.status = "OUT"
For each of these queries, I strongly suggest the following index:
ALTER TABLE table
ADD INDEX `LatestSerialStatus` (serial_number,date)
I use this type of query a lot in my own work, and have the above index as the primary key on tables. Query performance is extremely fast in such cases, for these type of queries.
See also the documentation on this query type.

SQL : Select statement order by

i want to select a column but with diferent order :
i have 2 table :
table_name:
+------+-----------+
| id | name |
+------+-----------+
| 1 | Sindra |
| 2 | Auli |
| 3 | Brian |
| 4 | Bina |
| 5 | zian |
| 6 | Bri |
| 7 | Andre |
+------+-----------+
table_temp, id_temp_name foreign key of id(table_name) :
+------+--------------+
| id | id_temp_name |
+------+--------------+
| 1 | 1 |
| 2 | 3 |
| 3 | 4 |
| 4 | 2 |
+------+--------------+
with this query :
SELECT *
FROM table_name
WHERE id IN
(SELECT id_temp_name FROM table_temp)
the result is always same look with table_name, i was looking for result that exactly same with id_temp_name order , so the result will be :
+------+-----------+
| id | name |
+------+-----------+
| 1 | Sindra |
| 3 | Brian |
| 4 | Bina |
| 2 | Auli |
+------+-----------+
thanks for any advice, .
You need to rewrite the query to be a JOIN between both tables, then you can set an ordering based on any column involved, even when not in the final result set:
SELECT table_name.id,
table_name.name
FROM table_name
INNER JOIN table_temp ON table_name.id = table_temp.id_temp_name
ORDER BY table_temp.id ;
Use a join instead of a sub-query.
SELECT table_name.id, table_name.name
FROM table_name
INNER JOIN table_temp ON table_name.id = table_temp.id
ORDER BY table_temp.id_temp_name
And... usually best to list the fields explicitly instead of using * to select all.
You should use a simple JOIN to achieve your result.
Your query:
SELECT *
FROM table_name
WHERE id IN
(SELECT id_temp_name FROM table_temp)
actually returns all the rows and columns of the table_name Table. So you won't get the desired id_temp_name results, since it's in a different table. That's why, you should use LEFT JOIN, since your left table is table_name, and your right table is table_temp, and you want to show data from a column of table_temp, which is id_temp_name.
So, what you need to do, is this:
SELECT tn.id, tn.name
FROM table_name AS tn
LEFT JOIN table_temp AS tt
ON tn.id= tt.id_temp_name
GROUP BY tn.id

mySQL record with latest status=1 in second table

I have table A
| id | Name |
_________________________
| 1 | ABC |
_________________________
| 2 | BCD |
_________________________
Table B
| id | a_id | Status | timestamp |
__________________________________________________________
| 1 | 1 | 1 | timestamp |
__________________________________________________________
| 2 | 1 | 2 | timestamp |
__________________________________________________________
| 3 | 1 | 3 | timestamp |
__________________________________________________________
| 4 | 2 | 1 | timestamp |
__________________________________________________________
In above example I only want to pick
| name | a_id | Status | timestamp |
__________________________________________________________
| BCD | 2 | 1 | timestamp |
So with any record that has ONLY and ONLY latest STATUS as 1, I want to pick that record.. If the LATEST STATUS is 2 or 3, I don't want ot pick them...
If possible I don't want to use sub-query because I am using Codeigniter which doesn't really like subqueries.
Please help
So that will be:
SELECT
A.name,
B.*
FROM
(SELECT
a_id,
MAX(`timestamp`) AS max_timestamp
FROM B
GROUP BY a_id) AS latest
LEFT JOIN B
ON
B.a_id=latest.a_id
AND
B.`timestamp`=latest.max_timestamp
LEFT JOIN A
ON B.a_id=A.id
WHERE
B.status=1
As for subquery - I doubt you can retrieve desired information without that in single query. That is because you need to use row set, which is a result of grouping by one column, in reference to another column. And so you need to group that first, and then apply JOIN to result row set.
Thanks to #Strawberry for pointing to this article, I figured a way to do it for this example.
select a.name
,b1.a_id
,b1.status
,b1.`timestamp` AS b1Time
FROM TableA a
JOIN TableB b1 ON b1.a_id = a.id
LEFT JOIN TableB b2 ON b2.a_id = b1.a_id AND b1.`timestamp` < b2.`timestamp`
WHERE b2.`timestamp` IS NULL
AND b1.status = 1
See this Fiddle.