Find two table where one table has redundant value - mysql

user_list
+---------------------+
+ user_id + name +
+---------------------+
+ 001 + Anna +
+---------------------+
+ 004 + David +
+----------------------
...
job_list
+---------------------+
+ user_id + job +
+---------------------+
+ 001 + Norse +
+---------------------+
+ 002 + Doctor +
+---------------------+
+ 003 + Sales +
+----------------------
+ 004 + Driver +
+----------------------
...
I want to find the records which job_list exists but user_list doesn't exists
For example, some user may deleted for some reason
but their record still stay in the database, so I want to find them and remove
In this case, the returning result should be 002 and 003
SELECT * FROM `user_list` ul, `job_list` jl WHERE jl.user_id NOT LIKE ul.user_id;
I tried the SQL command above but not work, please help

SELECT * FROM job_list AS jl
LEFT JOIN user_list AS ul ON ul.user_id = jl.user_id
WHERE ul.user_id IS NULL
should do the job

Try this
SELECT *
FROM `job_list` jl
WHERE NOT EXISTS (SELECT 1 FROM user_list ul WHERE ul.user_id=jl.user_Id)

If you dont care abaut know what records remove and you just whant to remove them something like this should do the trick: (warning: subquery´s are extremly inefficient)
delete from job_list where user_id not in ( select user_id from user_list)
if you whant to know the records before remove them you shuld join the tables:
SELECT * FROM job_list
LEFT JOIN user_list
ON user_list .user_id = job_list.user_id
WHERE user_list.user_id = NULL

SELECT user_id FROM job_list MINUS SELECT user_id FROM user_list;

Related

Creating a Stored Procedure to RANK Multiple Calculated Fields in MySQL 5.5.x

I know MS SQL fairly well (no expert), but am new to MySQL and can't quite get this to work. I have a game that is composed of 6 stages which are timed. There are penalties for misses and procedural errors where each miss adds 5 seconds, and each procedural adds 10 seconds.
I have to return a table that calculates total time for each stage, a rank for each stage's total time, total match time, total match misses, and overall match rank, in addition to all of the existing fields in the table, where a MatchId equals a specified match Id. I should note that I am forced to use a mySQL 5.5x database, so no window functions such as RANK() function.
Here is my partial table, as I mentioned there are 6 stages per match.
+---------+--------+------------+---------+-------+-------+---------+-------+-------+-------------+
| MatchID | UserID | CategoryID | Stg1Raw | Stg1M | Stg1P | Stg2Raw | Stg2M | Stg2P | ...-> Stg6p |
+---------+--------+------------+---------+-------+-------+---------+-------+-------+-------------+
| 1 | 1 | 9 | 30.75 | NULL | NULL | 28.47 | 1 | NULL | NULL |
| 1 | 2 | 15 | 24.07 | 3 | NULL | 22.20 | NULL | NULL | NULL |
| 1 | 3 | 12 | 23.96 | NULL | NULL | 22.87 | NULL | NULL | NULL |
| 2 | 2 | 15 | 25.72 | 1 | 1 | 28.95 | NULL | 1 | NULL |
+---------+--------+------------+---------+-------+-------+---------+-------+-------+-------------+
I am trying to create a stored procedure that takes in a parameter for matchId i.e. p_matchId and returns the scores for the match. What I can do is: create a stored procedure that calculates total time for each player for each stage, as well as calculating Total time for each player for all stages. I even figured out how to rank each stage as well as the total overall ranking in a standard query. I just cant put it all together.
Here is my query for calculating stage and overall total scores
SELECT sc.*,
(sc.stg1Total + sc.stg2Total + sc.stg3Total +
sc.stg4Total + sc.stg5Total + sc.stg6Total) AS matchTotal
FROM (SELECT m.Alias, c.Category,
(ifnull(s.stg1M,0) + ifnull(s.stg2M,0) + ifnull(s.stg3M,0) +
ifnull(s.stg4M,0) + ifnull(s.stg5M,0) + ifnull(s.stg6M,0)) AS mTotal,
s.stg1Raw, s.stg1M, s.stg1P, ((s.stg1Raw + (ifnull(s.stg1M,0) * 5)) + (ifnull(s.stg1P,0) * 10)) AS stg1Total,
s.stg2Raw, s.stg2M, s.stg2P, ((s.stg2Raw + (ifnull(s.stg2M,0) * 5)) + (ifnull(s.stg2P,0) * 10)) AS stg2Total,
s.stg3Raw, s.stg3M, s.stg3P, ((s.stg3Raw + (ifnull(s.stg3M,0) * 5)) + (ifnull(s.stg3P,0) * 10)) AS stg3Total,
s.stg4Raw, s.stg4M, s.stg4P, ((s.stg4Raw + (ifnull(s.stg4M,0) * 5)) + (ifnull(s.stg4P,0) * 40)) AS stg4Total,
s.stg5Raw, s.stg5M, s.stg5P, ((s.stg5Raw + (ifnull(s.stg5M,0) * 5)) + (ifnull(s.stg5P,0) * 50)) AS stg5Total,
s.stg6Raw, s.stg6M, s.stg6P, ((s.stg6Raw + (ifnull(s.stg6M,0) * 5)) + (ifnull(s.stg6P,0) * 60)) AS stg6Total
FROM Scores s
INNER JOIN Members m
ON s.ShooterId = m.id
INNER JOIN Categories c
ON s.CatId = c.ID
where (MatchID = p_matchId)) AS sc
I know there is probably a better/shorter way to write the above query so any suggestions on that would be appreciated as well.
I tried putting results of the above query into a temporary table and then run the ranking for each stage on the temp table, but mySQL doesn't appear to like the way I was attempting to do it. What would be the shortest most efficient way to create this SP?
UPDATE: To clarify, How could I create a rank column for each of the following in the above sql statement: stg1Total, stg2Total, stg3Total, stg4Total, stg5Total, stg6Total, and matchTotal?
UPDATE 2: I got it working but had to create multiple temp tables to get it to run, and achieve the desired results
BEGIN
CREATE TEMPORARY TABLE A SELECT sc.*,
(sc.stg1Total + sc.stg2Total + sc.stg3Total +
sc.stg4Total + sc.stg5Total + sc.stg6Total) AS matchTotal
FROM
(SELECT m.Alias, c.Category, (ifnull(s.stg1M,0) + ifnull(s.stg2M,0) + ifnull(s.stg3M,0) +
ifnull(s.stg4M,0) + ifnull(s.stg5M,0) + ifnull(s.stg6M,0)) AS mTotal,
s.stg1Raw, s.stg1M, s.stg1P,
((s.stg1Raw + (ifnull(s.stg1M,0) * 5)) + (ifnull(s.stg1P,0) * 10)) AS stg1Total,
s.stg2Raw, s.stg2M, s.stg2P,
((s.stg2Raw + (ifnull(s.stg2M,0) * 5)) + (ifnull(s.stg2P,0) * 10)) AS stg2Total,
s.stg3Raw, s.stg3M, s.stg3P,
((s.stg3Raw + (ifnull(s.stg3M,0) * 5)) + (ifnull(s.stg3P,0) * 10)) AS stg3Total,
s.stg4Raw, s.stg4M, s.stg4P,
((s.stg4Raw + (ifnull(s.stg4M,0) * 5)) + (ifnull(s.stg4P,0) * 40)) AS stg4Total,
s.stg5Raw, s.stg5M, s.stg5P,
((s.stg5Raw + (ifnull(s.stg5M,0) * 5)) + (ifnull(s.stg5P,0) * 50)) AS stg5Total,
s.stg6Raw, s.stg6M, s.stg6P,
((s.stg6Raw + (ifnull(s.stg6M,0) * 5)) + (ifnull(s.stg6P,0) * 60)) AS stg6Total
FROM Scores s
INNER JOIN Members m
ON s.ShooterId = m.id
INNER JOIN Categories c
ON s.CatId = c.ID
where (MatchID = p_matchId)) AS sc;
CREATE TEMPORARY TABLE B SELECT * FROM A;
CREATE TEMPORARY TABLE C SELECT * FROM A;
CREATE TEMPORARY TABLE D SELECT * FROM A;
CREATE TEMPORARY TABLE E SELECT * FROM A;
CREATE TEMPORARY TABLE F SELECT * FROM A;
CREATE TEMPORARY TABLE G SELECT * FROM A;
CREATE TEMPORARY TABLE H SELECT * FROM A;
SELECT Alias, Category, mTotal, stg1Raw, stg1M ,stg1P, stg1Total,
(SELECT COUNT(*)+1 FROM B WHERE A.stg1Total>B.stg1Total)AS stg1Rnk,
stg2Raw, stg2M ,stg2P, stg2Total,
(SELECT COUNT(*)+1 FROM C WHERE A.stg2Total>C.stg2Total)AS stg2Rnk,
stg3Raw, stg3M ,stg3P, stg3Total,
(SELECT COUNT(*)+1 FROM D WHERE A.stg3Total>D.stg3Total)AS stg3Rnk,
stg4Raw, stg4M ,stg4P, stg4Total,
(SELECT COUNT(*)+1 FROM E WHERE A.stg4Total>E.stg4Total)AS stg4Rnk,
stg5Raw, stg5M ,stg5P, stg5Total,
(SELECT COUNT(*)+1 FROM F WHERE A.stg5Total>F.stg5Total)AS stg5Rnk,
stg6Raw, stg6M ,stg6P, stg6Total,
(SELECT COUNT(*)+1 FROM G WHERE A.stg6Total>G.stg6Total)AS stg6Rnk,
matchTotal,
(SELECT COUNT(*)+1 FROM H WHERE A.matchTotal>H.matchTotal)AS oaRnk
FROM A;
DROP TEMPORARY TABLE A;
DROP TEMPORARY TABLE B;
DROP TEMPORARY TABLE C;
DROP TEMPORARY TABLE D;
DROP TEMPORARY TABLE E;
DROP TEMPORARY TABLE F;
DROP TEMPORARY TABLE G;
DROP TEMPORARY TABLE H;
END
Its a little unruly, an probably not that performant, So I would still like to see a more efficient and performant method of achieving the same results.

SQL Query, how to select a interval where the next value is current value +1, identifying the next gap and leave everything else untouched?

I'm making a program on java using sql querys and i'd like to update a sequence of ids adding 1 to each, but only in the sequence interval.
Let's say i have a table like this:
1 - a | 2 - b | 3 - c | 5 - e | 6 - f
And i'd like to "push" a's ID and subsequents forward by 1, but only while the difference between one another is still 1. The result would be like this:
2 - a | 3 - b | 4 - c | 5 - e | 6 - f
I know it is possible to do programatically, but is there a way to do this with SQL querys? If not, what would be the best way to do this?
Thanks in advance.
Edit: Found the answer thanks to Thorsten Kettner, my query ended up like this:
update pair set id = id + 1
where id >= 1
and id <
(
select min(id)
from (select * from pair) as gap
where gap.id > 1 and not exists
(
select *
from (select * from pair) as oneless
where oneless.id = gap.id - 1
)
)
order by id desc
If you know where the gap is that you want closed (ID 4 in your case) use this:
update mytable set id = id + 1 where id < 4;
If you don't know where the gap is, find it:
select min(id) - 1
from mytable
where not exists
(
select *
from mytable oneless
where oneless.id = mytable.id - 1
);
We can combine both statments now to do the update:
update mytable set id = id + 1
where id <
(
select min(id) - 1
from mytable
where not exists
(
select *
from mytable oneless
where oneless.id = mytable.id - 1
)
);
Let's call your table Pair and the two columns Key and Value. Your query would be something like this:
update p1
set Key = p1.Key + 1
from Pair as p1
inner join Pair as p2
on p2.Key = p1.Key + 1
PS. I'm assuming your result has a typo because after the update, f should have a value of 7.

MySQL select user who less performed an action

I have the following table structures:
ACTIONS
+ userid + action +
+----------+----------+
USERS
+ id + name +
+----------+----------+
+ 1 + james +
+----------+----------+
+ 2 + john +
+----------+----------+
A data example:
ACTIONS
+ userid + action +
+----------+----------+
+ null + action1 +
+----------+----------+
+ 1 + action2 +
+----------+----------+
+ 1 + action3 +
+----------+----------+
+ 2 + action4 +
+----------+----------+
I need a SELECT query that will return the user who performed less actions.
If all fields are null (the first launch) or all equal (all users performed the same action), it can return 1 user (rand, asc, desc, it's the same).
///////////////////
EDIT
Based on the Richard Hamilton's reply, this query only works with users already in ACTIONS table. If one or more user_id are NULL or users are not in ACTIONS, doesn't select from USERS table
SELECT id FROM users
INNER JOIN actions ON users.id = actions.user_id
GROUP BY user_id
ORDER BY COUNT(user_id)
LIMIT 1;
You'll want to use GROUP BY. We can then ORDER BY the number of actions they performed.
To find the one with the lowest number, just use LIMIT 1
SELECT user_id FROM users
INNER JOIN data ON users.id = data.users_id
GROUP BY action
ORDER BY COUNT(action)
LIMIT 1;

Select inner join with n_m table

I have three tables as following:
USERS TABLE
id_user| name |
---------------
1 | ...
2 | ...
SERVICES TABLE
id_service | name |
-------------------
1 | ...
2 | ...
3 | ...
USER_SERVICES TABLE (n-m)
id_user | id_service
--------------------
1 | 1
1 | 2
2 | 1
And I need to do a SELECT starting from "SELECT * FROM users" and then, getting the users by services. Ex. I need to get every user with services = 1 and services = 2 (and maybe he has other more services, but 1 and 2 for sure).
I did the following:
SELECT *
FROM `users`
INNER JOIN user_services ON users.id_user = user_services.id_user
WHERE id_service=1 AND id_service=2
But this, of course dont works since there is not a single record matching service = 1 and service = 2.
What can I do?
Add an extra join for the other service you want to check:-
SELECT *
FROM `users`
INNER JOIN user_services us1 ON users.id_user = us1.id_user AND us1.id_service=1
INNER JOIN user_services us2 ON users.id_user = us2.id_user AND us2.id_service=2
select t.*,
(select count(*) from user_services where id_user = t.id_user) how_much
from users t;
Is this what you want???
It shows the data of the users and how much services are in the services table. Other possibility is this:
select t.*,
(case when (select count(*)
from user_services where id_user = 1) > 0
then 'service1'
else 'null'
end) has_service_1
from users t;
The problem with this select is that you have to repeat this case...end as much times as id_services you have, so it doesn't make sense if the number of services is increasing over time. On the contrary, if it is a somewhat fixed number, and it is not a big number, this could be a solution.

slow count query - explain shows temp and filesort

have following query which runs slow,
It produces a list of ACTION records with "action.typeid=1"
and also counts if an ACTION record with "typeid=2" exists.
It is this count which is slowing things - it uses temporary and filesort!!!
can you help me find out how to improve.
EXPLAIN
SELECT
action.actionid
FROM
ACTION
INNER JOIN EVENT
ON action.eventid = event.eventid
LEFT JOIN
(SELECT
COUNT(1),
action.eventid
FROM
ACTION
WHERE (action.typeid = '2')
GROUP BY action.eventid) AS act
ON act.eventid = event.eventid
WHERE actiondate2 BETWEEN 20130601
AND 20131031
AND event.siteid = 1
AND action.typeid = 1
The following indexes exist
CREATE INDEX idx_cusid ON `event` (cusid);
CREATE INDEX idx_actiontypeid ON `action` (typeid);
CREATE INDEX idx_actioneventid ON `action` (eventid);
CREATE INDEX idx_actiondate ON `action` (actiondate2);
CREATE INDEX idx_eventsiteid ON `event` (siteid);
Sir, the requirements in the question are still unclear to me.
I am going to explain my confusion using examples.
Please take a look at a simple demo: http://sqlfiddle.com/#!2/19f52c/6
This demo contains a simplified (for a sake of clarity) database structure and queries.
The query from the question (the first query in the demo) returns the following results:
SELECT action.actionid
FROM ACTION
INNER JOIN EVENT
ON action.eventid = event.eventid
LEFT JOIN
(SELECT
COUNT(1),
action.eventid
FROM
ACTION
WHERE (action.typeid = '2')
GROUP BY action.eventid) AS act
ON act.eventid = event.eventid
WHERE -- actiondate2 BETWEEN 20130601 AND 20131031 AND
event.siteid = 1
AND action.typeid = 1
;
+ ------------- +
| actionid |
+ ------------- +
| 1 |
| 3 |
| 5 |
+ ------------- +
Hovever, in the above query the subquery with alias ACT is simply ... useless.
The query executes this subquery (consuming time and server resources), then ... ignores its results, just throws them away.
The above query is equivalent to the below query (the second query in the demo) - it returns identical results as the query from the question, but without using the subquery (saving time and resources, therefore will perform much better):
SELECT action.actionid
FROM
ACTION
INNER JOIN EVENT
ON action.eventid = event.eventid
WHERE -- actiondate2 BETWEEN 20130601 AND 20131031 AND
event.siteid = 1
AND action.typeid = 1
;
+ ------------- +
| actionid |
+ ------------- +
| 1 |
| 3 |
| 5 |
+ ------------- +
If your intent is to optimize the query show in your question - then please simply use the query shown above, this is the answer to your question.
However, looking at your comments about expected results, it appears that the query in the question is probably wrong - it doesn't give expected results.
Well, but it's still unclear what the query should give ? There are many possibilities, I'll show some of them below.
If you need to list all action.actionid with typeid = 1, but only such records, for which it exists any record with the same eventid and typeid = 2 ... then use the below query (the 3rd query in the demo):
SELECT a.actionid
FROM
ACTION a
INNER JOIN EVENT e
ON a.eventid = e.eventid
WHERE -- actiondate2 BETWEEN 20130601 AND 20131031 AND
EXISTS ( SELECT 1 FROM action a1
WHERE a1.eventid = a.eventid
AND a1.typeid = 2
)
AND e.siteid = 1
AND a.typeid = 1
;
+ ------------- +
| actionid |
+ ------------- +
| 1 |
| 3 |
+ ------------- +
This query uses the EXISTS operator, instead of COUNT() - if we want an information that same record exist, we don't need to count all of them! The count must read all of records, the EXISTS stops reading the table if it finds the first record that meets conditions - therefore EXISTS is usually faster than COUNT().
If you need to list all action.actionid with typeid = 1,and also display an information that some corresponding records exist with typeid = 2 - then use the below query (the fourth query in the demo):
SELECT
a.actionid ,
CASE WHEN EXISTS ( SELECT 1 FROM action a1
WHERE a1.eventid = a.eventid
AND a1.typeid = 2
)
THEN 'typeid=2 EXISTS'
ELSE 'typeid=2 DOESN''T EXIST'
END typeid2_exist
FROM
ACTION a
INNER JOIN EVENT e
ON a.eventid = e.eventid
WHERE -- actiondate2 BETWEEN 20130601 AND 20131031 AND
e.siteid = 1
AND a.typeid = 1
;
+ ------------- + ---------------------- +
| actionid | typeid2_exist |
+ ------------- + ---------------------- +
| 1 | typeid=2 EXISTS |
| 3 | typeid=2 EXISTS |
| 5 | typeid=2 DOESN'T EXIST |
+ ------------- + ---------------------- +
But if you really need to count corresponding records with typeid = 2 - then this query can help (the fifth query in the demo):
SELECT
a.actionid ,
( SELECT count(*) FROM action a1
WHERE a1.eventid = a.eventid
AND a1.typeid = 2
) typeid2_count
FROM
ACTION a
INNER JOIN EVENT e
ON a.eventid = e.eventid
WHERE -- actiondate2 BETWEEN 20130601 AND 20131031 AND
e.siteid = 1
AND a.typeid = 1
;
+ ------------- + ------------------ +
| actionid | typeid2_count |
+ ------------- + ------------------ +
| 1 | 1 |
| 3 | 1 |
| 5 | 0 |
+ ------------- + ------------------ +
If none of queries shown above meet your requirements, please show (basing on sample data in the demo) the results that the query should return - this helps someone in this forum to build a proper query that meets all your requirements.
Then, when we recognize the right query that meet all expectations, we can start to optimize it's performance.