What exactly does EXISTS do in MySQL? - mysql

I read that the output of the subquery doesn't matter and only its existence matter. But, when I change the code in the subquery, why is my output changing?
These are the tables:
mysql> select * from boats;
+------+-----------+-------+
| bid | bname | color |
+------+-----------+-------+
| 101 | Interlake | blue |
| 102 | Interlake | red |
| 103 | Clipper | green |
| 104 | Marine | red |
+------+-----------+-------+
mysql> select * from sailors;
+------+---------+--------+------+
| sid | sname | rating | age |
+------+---------+--------+------+
| 22 | Dustin | 7 | 45 |
| 29 | Brutus | 1 | 33 |
| 31 | Lubber | 8 | 55.5 |
| 32 | Andy | 8 | 25.5 |
| 58 | Rusty | 10 | 35 |
| 64 | Horatio | 7 | 35 |
| 71 | Zorba | 10 | 16 |
| 74 | Horatio | 9 | 40 |
| 85 | Art | 3 | 25.5 |
| 95 | Bob | 3 | 63.5 |
+------+---------+--------+------+
10 rows in set (0.00 sec)
mysql> select * from reserves;
+------+------+------------+
| sid | bid | day |
+------+------+------------+
| 22 | 101 | 1998-10-10 |
| 22 | 102 | 1998-10-10 |
| 22 | 103 | 1998-10-08 |
| 22 | 104 | 1998-10-08 |
| 31 | 102 | 1998-11-10 |
| 31 | 103 | 1998-11-06 |
| 31 | 104 | 1998-11-12 |
| 64 | 101 | 1998-09-05 |
| 64 | 102 | 1998-09-08 |
| 74 | 103 | 1998-09-08 |
+------+------+------------+
select sname from sailors s where exists(select * from reserves r where r.bid=103);
+---------+
| sname |
+---------+
| Dustin |
| Brutus |
| Lubber |
| Andy |
| Rusty |
| Horatio |
| Zorba |
| Horatio |
| Art |
| Bob |
+---------+
10 rows in set (0.00 sec)
mysql> select sname from sailors s where exists(select * from reserves r where r.bid=103 and r.sid=s.sid);
+---------+
| sname |
+---------+
| Dustin |
| Lubber |
| Horatio |
+---------+
Also, I am not able to understand what r.sid=s.sid is doing here. All the sid in reserves are already from sailors table. Please someone explain it to me.

The EXISTS is a Boolean Operator which indicates that if there is ANY row in the sub-query you passed to it. When you execute this:
EXISTS(SELECT * FROM reserves r WHERE r.bid=103)
It will return TRUE after finding the FIRST row which has the condition bid = 103 in Reserves table. The first part of the query doesn't matter, it does not matter what you SELECT in Exists and MySQL engine will ignore it, just the WHERE clause is the part which makes the difference, you can use Exists even like this:
EXISTS(SELECT 1 FROM reserves r WHERE r.bid=103)
In the query above, nothing depends on the values in main query, nothing depends on Sailors table, and if there is ANY row in the Reserves table with bid = 103, then it always will return TRUE.
In the second sub-query with EXISTS, you have a different WHERE clause, and it depend on the value of the fields of the main Query, so it will have different result per each row:
EXISTS(SELECT * FROM reserves r WHERE r.bid=103 AND r.sid=s.sid)
In the above query, per each row in Sailors table, MySQL uses sid value to produce the WHERE condition of the sub-query in EXISTS operator, so it will returns TRUE for a row in Sailors table if there are ANY rows in Reserves table which has a bid = 103 and sid = Sailors.sid, and it will returns False for those that has not such a record in Reserves table, and finally you will get a different result

I think I got that. Exists is used to check if the subquery is existing for the main query. I didn't give any link for the main query and subquery in the first query.
For every name in sailors, independently, the subquery is existing. Hence, I got all the names. In the second query, I added s.sid=r.sid which links the main query and subquery. It checks if for a sname, if bid=103, and also, if s.sid=r.sid.
Please comment if I got that right.

Related

mysql statement not working: DELETE FROM user_info WHERE username='t2';

Hi I'm trying to delete a row from mysql using the primary key. I've looked up a couple of examples and exactly followed the syntax, but mysql table is not getting affected.
I'm using:
DELETE FROM user_info WHERE username='t2';
mysql reponse:
Query OK, 0 rows affected (0.00 sec)
Screetshot of mysql table
seems you have single quote arount t2 in db 't2'
then try using
DELETE FROM user_info WHERE username="'t2'" ;
or
DELETE FROM user_info WHERE username like concat('%','t2', '%') ;
if you really have single quote saved in db arout t2 .. the you should avoid this kind of storage behaviour is really a bad practice store a value with (unuseful ) quote around
+----+------+------------+
| ID | NUM | CREATED |
+----+------+------------+
| 1 | 11 | 2018-01-01 |
| 2 | 22 | 2018-02-01 |
| 3 | 11 | 2018-03-01 |
| 4 | 44 | 2018-04-01 |
| 5 | 22 | 2018-05-01 |
| 6 | 44 | 2018-04-02 |
+----+------+------------+
DELETE from numbers where ID = 1;
+----+------+------------+
| ID | NUM | CREATED |
+----+------+------------+
| 2 | 22 | 2018-02-01 |
| 3 | 11 | 2018-03-01 |
| 4 | 44 | 2018-04-01 |
| 5 | 22 | 2018-05-01 |
| 6 | 44 | 2018-04-02 |
+----+------+------------+

MySQL retrieve last record in JOIN with group by

I have following tables with data as:
1.Table follow_up as :
mysql> select * from follow_up;
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
| follow_up_id | feedback_close | feedback_open | is_email_required | is_Open | reminder_date | client_id | conclusion_id | stage_id |
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
| 1 | NULL | dsffsdfsdfsd | 1 | 1 | 2017-09-20 | 101 | 96 | 72 |
| 2 | NULL | FSGDFHFGHFG | 1 | 1 | 2017-09-28 | 101 | 251 | 72 |
| 3 | NULL | Tender stage fb | 0 | 1 | NULL | 101 | 98 | 163 |
| 4 | NULL | Call back tender stage update date from 28 to 30 | 1 | 1 | 2017-09-28 | 101 | 96 | 163 |
| 5 | NULL | Metting follow up for next meeting | 1 | 1 | 2017-10-02 | 101 | 96 | 73 |
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
2. Table logs as :
mysql> SELECT * from logs where transaction = 'FLWUP';
+---------+---------+---------------------+---------+-------------+
| user_id | menu_id | logs_time | tran_id | transaction |
+---------+---------+---------------------+---------+-------------+
| 84 | 69 | 2017-09-19 19:31:04 | 1 | FLWUP |
| 84 | 69 | 2017-09-19 19:31:25 | 2 | FLWUP |
| 84 | 69 | 2017-09-20 19:10:41 | 2 | FLWUP |
| 84 | 69 | 2017-09-21 12:35:01 | 3 | FLWUP |
| 84 | 69 | 2017-09-21 12:35:26 | 4 | FLWUP |
| 84 | 69 | 2017-09-21 12:36:16 | 4 | FLWUP |
| 84 | 69 | 2017-09-21 12:38:30 | 5 | FLWUP |
+---------+---------+---------------------+---------+-------------+
7 rows in set (0.00 sec)
3. table allcode as :
mysql> select * from allcode where code_type like 'MARK%';
+------------------+---------+------+----------------------+
| code_type | code_id | srno | code_name |
+------------------+---------+------+----------------------+
| MARKETING_STAGES | 72 | 1 | Enquiry |
| MARKETING_STAGES | 73 | 3 | Meeting |
| MARKETING_STAGES | 74 | 4 | Presentation |
| MARKETING_STAGES | 163 | 2 | Tender |
+------------------+---------+------+----------------------+
11 rows in set (0.00 sec)
I have invoked a query and got result as :
mysql> select f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
where l.transaction='FLWUP' and f.client_id = 101
group by ast.code_name order by ast.srno;
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
| follow_up_id | feedback_open | feedback_close | reminder_date | stage | conclusion | max(l.logs_time) |
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
| 1 | dsffsdfsdfsd | NULL | 2017-09-20 | Enquiry | Call Back | 2017-09-20 19:10:41 |
| 3 | Tender stage fb | NULL | NULL | Tender | Next | 2017-09-21 12:36:16 |
| 5 | Metting follow up for next meeting | NULL | 2017-10-02 | Meeting | Call Back | 2017-09-21 12:38:30 |
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
3 rows in set (0.00 sec)
But I want result as :
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
| follow_up_id | feedback_open | feedback_close | reminder_date | stage | conclusion | max(l.logs_time) |
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
| 2 | FSGDFHFGHFG | NULL | 2017-09-20 | Enquiry | Call Back | 2017-09-20 19:10:41 |
| 4 | Call back tender stage update date from 28 to 30 | NULL | NULL | Tender | Next | 2017-09-21 12:36:16 |
| 5 | Metting follow up for next meeting | NULL | 2017-10-02 | Meeting | Call Back | 2017-09-21 12:38:30 |
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
3 rows in set (0.00 sec)
I'm not able to JOIN and group by to get required result.
column conclusion_id and stage_id of table follow_up are referring to code_id of table allcode.
Question :
the result I want is to be
group by stage_id,
order by srno of allcode and
last/recent follow_up_id of follow_up table
DEMO Includes my answer, original question with full group by needed, and Reupal's answer in demo. You were missing the values in your sample data for conclusionID so I just created them based on ID (now updated to ISO, Callback but missing 98.)
and my results don't match yours in this column; but I believe your expected results are in error.
Seems like you want the max follow_up_ID for each stage_ID when multiple stage_ID's exist
This can be handled by a derived table/inline view getting that max follow_UP_ID grouped by the stage_ID and a joining it back to your set. to limit results to include only the max follow_Up_ID by stage_Id.
I'm also not a fan of mySQL's extended group by and prefer including all columns not aggregated in the select in the group by. Using the extended group by tends to hide potential problems. In this case grouping by just the ast.code_name allowed the engine to select a non distinct value from the other columns. You ended up not getting the desired results and furthermore it hide the fact you would get multiple records in your query were it not for the extended group by use/misuse.
SELECT f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
JOIN SELECT max(follow_up_ID) MFID, stage_ID
FROM follow_up
GROUP BY stage_ID) Z
on f.follow_up_ID = Z.MFID
and F.Stage_ID = Z.Stage_ID
WHERE l.transaction='FLWUP' and f.client_id = 101
GROUP BY f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name , ac.code_name
ORDER BY ast.srno;
Try below, notice ordering and group by sequence.
select f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
where l.transaction='FLWUP' and f.client_id = 101
group by follow_up.stage_id order by ast.srno, follow_up.follow_up_id DESC;
This should works, and if its not then you should search like how to set ordering on multiple column.
Ref. article- SQL multiple column ordering

How to show table and total cost together?

I am trying to show a list of costs and have the total at the bottom of my list:
mysql> select title_name, rental_transaction, renter_lname,renter_fname, rental_cost
-> from rentals
-> where rental_date between '161201' and '161219'
-> and DATEDIFF(date(rental_return_date ), date(rental_date )) > 7;
+----------------------------+--------------------+--------------+--------------+-------------+
| title_name | rental_transaction | renter_lname | renter_fname | rental_cost |
+----------------------------+--------------------+--------------+--------------+-------------+
| WarioWare Touched! | 13 | Brennan | Kathleen | 2.99 |
| Hot Shots Golf: Open Tee | 23 | Grey-Gubler | Eva | 3.99 |
| WarioWare Touched! | 29 | Smithers | Kieran | 2.99 |
| The Urbz: Sims in the City | 56 | Winters | Emily | 4.99 |
| Lumines: Puzzle Fusion | 68 | Ryan | Rebecca | 3.99 |
| WarioWare Touched! | 89 | Byrne | Ann | 2.99 |
+----------------------------+--------------------+--------------+--------------+-------------+
6 rows in set (0.00 sec)
I basically want this table, but I want to show the total cost of the rentals at the bottom of the table and am wondering if this is possible to do?
You can do this using UNION.
But as UNION needs the two queries to have the same number of columns you will need to add some null as columnName
The query:
select title_name, rental_transaction, renter_lname,renter_fname, rental_cost
from rentals
where rental_date between '161201' and '161219'
and DATEDIFF(date(rental_return_date ), date(rental_date )) > 7;
union
select 'total_cost', null as columnName, null as columnName, null as columnName, sum(rental_cost)
from rentals
The result:

Remove duplicates SQL while ignoring key and selecting max of specified column

I have the following sample data:
| key_id | name | name_id | data_id |
+--------+-------+---------+---------+
| 1 | jim | 23 | 098 |
| 2 | joe | 24 | 098 |
| 3 | john | 25 | 098 |
| 4 | jack | 26 | 098 |
| 5 | jim | 23 | 091 |
| 6 | jim | 23 | 090 |
I have tried this query:
INSERT INTO temp_table
SELECT
DISTINCT #key_id,
name,
name_id,
#data_id FROM table1,
I am trying to dedupe a table by all fields in a row.
My desired output:
| key_id | name | name_id | data_id |
+--------+-------+---------+---------+
| 1 | jim | 23 | 098 |
| 2 | joe | 24 | 098 |
| 3 | john | 25 | 098 |
| 4 | jack | 26 | 098 |
What I'm actually getting:
| key_id | name | name_id | data_id |
+--------+-------+---------+----------+
| 1 | jim | 23 | NULL |
| 2 | joe | 24 | NULL |
| 3 | john | 25 | NULL |
| 4 | jack | 26 | NULL |
I am able to dedupe the table, but I am setting the 'data_Id' value to NULL by attempting to override the field with '#'
Is there anyway to select distinct on all fields and while keeping the value for 'data_id'? I will take the highest or MAX data_id # if possible.
If you only want one row returned for a specific value (in this case, name), one option you have is to group by that value. This seems like a good approach because you also said you wanted the largest data_id for each name, so I would suggest grouping and using the MAX() aggregate function like this:
SELECT name, name_id, MAX(data_id) AS data_id
FROM myTable
GROUP BY name, name_id;
The only thing you should be aware of is the possibility that a name occurs multiple times under different name_ids. If that is possible in your table, you could group by the name_id too, which is what I did.
Since you stated you're not interested in the key_id but only the name, I just excluded it from the query altogether to get this:
| name | name_id | data_id |
+-------+---------+---------+
| jim | 23 | 098 |
| joe | 24 | 098 |
| john | 25 | 098 |
| jack | 26 | 098 |
Here is the SQL Fiddle example.
RENAME TABLE myTable to Old_mytable,
myTable2 to myTable
INSERT INTO myTable
SELECT *
FROM Old_myTable
GROUP BY name, name_id;
This groups my tables by the values I want to dedupe while still keeping structure and ignoring the 'Data_id' column

Inner/Outer join issue in MySQL

I have 2 tables orders and customers with data as:
mysql> select * from orders;
+---------+----------------+----------+-------+
| orderid | product | customer | price |
+---------+----------------+----------+-------+
| 292 | sofa | 101 | 6000 |
| 293 | table | 105 | 3000 |
| 294 | table | 104 | 1450 |
| 295 | table | 101 | 1200 |
| 296 | chair | 103 | 800 |
| 297 | dressing table | 104 | 9000 |
| 298 | corner | 102 | 1000 |
| 299 | corner | 102 | 900 |
| 300 | bed | NULL | NULL |
| 301 | door | NULL | NULL |
+---------+----------------+----------+-------+
10 rows in set (0.00 sec)
mysql> select * from customers;
+--------+----------+-----------+
| custid | name | location |
+--------+----------+-----------+
| 101 | jaspreet | New delhi |
| 102 | harpreet | Jalandhr |
| 103 | surjit | Amritsar |
| 104 | harneet | ludhiana |
| 105 | hashar | New Delhi |
| 106 | harneet | NULL |
+--------+----------+-----------+
6 rows in set (0.00 sec)
When i run inner join, it return the following data:
mysql> select orders.orderid,customers.name from orders inner join customers where orders.customer=customers.custid;
+---------+----------+
| orderid | name |
+---------+----------+
| 292 | jaspreet |
| 295 | jaspreet |
| 298 | harpreet |
| 299 | harpreet |
| 296 | surjit |
| 294 | harneet |
| 297 | harneet |
| 293 | hashar |
+---------+----------+
8 rows in set (0.00 sec)
But the outer joins are not working on this.
mysql> select orders.orderid,customers.name from orders left join customers where orders.customer=customers.custid;
ERROR 1064 (42000): You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use n ear 'where orders.customer=customers.custid' at line 1
Can someone help me with this concept?
Thanks in advance
You're looking for ON not WHERE:
SELECT orders.orderid,
customers.name
FROM orders
LEFT JOIN customers
ON orders.customer = customers.custid;
The JOIN condition is specified with ON, not WHERE:
SELECT orders.orderid,
customers.name
FROM orders
LEFT JOIN customers
ON orders.customer = customers.custid;
You should use this with both inner and outer joins. WHERE should be used for conditions on individual tables, not joining conditions.
You were able to get away without this for the inner join because a join without an ON clause performs a full cross product, and then the WHERE clause filters based on testing across this. I'm not sure why, but you can't do this with outer join.