Mysql multiple select statements in single query with if condition - mysql

i have table
users
with id,name,type,active,...
i have another table orders
with orderid,userid,...
i want to update orders table in such a way that
UPDATE orders SET userid=(SELECT id FROM users WHERE type="some" and active=1)
but my problem is
if SELECT id FROM users WHERE type="some" and active=1 doesnt have any result
i want to use
SELECT id FROM users WHERE type="some" limit 0,1
ie the first result
i can do this easly in any language like php/python etc but i just have access to mysql server so cannot do that
but how can i do in pure sql in single query
i tried if statement but not working

Here is one method using ORDER BY:
UPDATE orders o
SET userid = (SELECT u.id
FROM users u
WHERE u.type = 'some'
ORDER BY active DESC
LIMIT 1
);
This assumes that active only takes on the values 0 and 1. If there are other values, use ORDER BY (active = 1) DESC.
Performance should be fine with an index on users(type, active, id).
Another method uses aggregation and COALESCE():
UPDATE orders o
SET userid = (SELECT COALESCE(MAX(CASE WHEN active = 1 THEN u.id END),
MAX(u.id)
)
FROM users u
WHERE u.type = 'some'
);
I would expect the ORDER BY to be a wee bit faster, but sometimes MySQL surprises me with aggregations in correlated subqueries. That said, if you have very few rows for a given type, the performance difference may not be noticeable.

Related

How to group in MySQL

I am trying to group rows in my tables in MySQL
I have the following database setup:
I would like to get the event with the highest id which has a specific user_id (such as '3') either as the organiser.user_id or the helper.user_id.
I am not sure how to go around doing this as I am fairly new to MySQL.
Any help would be appreciated.
you can either join event_helpers or event_organisers to events table.
then specific user_id and order by event id get the top one
select
events.* from events
inner join event_helpers on (event_helpers.event_id=events.id)
where event_helpers.user_id=3
order by events.id desc limit 1
the following should do the trick. As there's no examplary data I cannot vouch for it entirely. also I haven't actually ran the code against a database so it could contain minor errors.
In short i'm doing:
select all from the events table.
join the organisers table on event_id
limit results to only show rows with a joined user_id of 3
order rows descending (highest first)
limit the result to retrieve only one
SELECT *
FROM events
LEFT JOIN event_organisers ON event_organisers.event_id = events.id
LEFT JOIN event_helpers ON event_helpers.event_id = events.id
WHERE event_organisers.user_id = 3
OR event_helpers.user_id = 3
ORDER BY events.id DESC
LIMIT 1;
This should get you the result you're looking for. Or at least it should get you going.
Such lookups are usually done with IN or EXISTS.
select *
from events
where id in (select event_id from event_helpers where user_id = 3)
or id in (select event_id from event_organisers where user_id = 3)
order by id desc
limit 1;
IN or EXISTS often perform better than joins (though I don't expect this to be the case with the given task), but queries with an OR condition are often rather slow, because indexes might not be used as would be desired. Another option that may or may not perform better:
select *
from
(
select *
from events
where id = (select max(event_id) from event_helpers where user_id = 3)
union all
select *
from events
where id = (select max(event_id) from event_organisers where user_id = 3)
) newest_two
order by id desc
limit 1;

Can I use a subquery to complete a where clause?

I have 2 tables joined by the membership.ID and memberDues.memberID. The membership table has a column, firstLast, which is unique for each row. If I have a value for firstLast, i should be able to then find the membership.ID and compare it to the memberDues.memberID. I am using the following query but it doesn't work:
SELECT * FROM memberDues, membership
WHERE memberID =
( SELECT ID FROM membership
WHERE firstLast = 'Jim Smith')
ORDER BY payDate DESC
LIMIT 1
The result gets the correct memberDues.memberID and the other data from its row but pulls an unrelated data set from the membership table where even the ID is wrong. What's wrong with the query?
You can join the two tables on their shared ID values and then you can select only those rows which have firstLast = 'Jim Smith'. I think you can achieve more efficiently what you want (avoiding one additional select) using the following query:
SELECT aa.*, bb.*
FROM memberDues AS aa
INNER JOIN membership AS bb
ON aa.memberID = bb.ID
WHERE bb.firstLast = 'Jim Smith'
ORDER BY aa.payDate DESC
LIMIT 1;
You should use IN instead of equal sign:
SELECT * FROM memberDues, membership WHERE memberID IN
(SELECT ID FROM membership WHERE firstLast = 'Jim Smith')
ORDER BY payDate DESC LIMIT 1
What's wrong with the query?
Your query joins 2 tables without specifying a condition to relate them.
For a fast start on your question, I'd try to specify
membership.ID = memberDues.memberID in the where clause.
My personal advice is to use a LEFT JOIN using that condition in the ON clause. But it's more advanced SQL coding, and choices depends on the structure and the needs of this particular application.

Why does a MySQL query with a Dependent Subquery take so much longer to execute than executing each statement individually?

I'm running two queries.
The first one gets unique IDs. This executes in ~350ms.
select parent_id
from duns_match_sealed_air_072815
group by duns_number
Then I paste those IDs into this second query. With >10k ids pasted in, it also executes in about ~350ms.
select term, count(*) as count
from companies, business_types, business_types_to_companies
where
business_types.id = business_types_to_companies.term_id
and companies.id = business_types_to_companies.company_id
and raw_score > 25
and diversity = 1
and company_id in (paste,ten,thousand,ids,here)
group by term
order by count desc;
When I combine these queries into one it takes a long time to execute. I don't know how long because I stopped it after minutes.
select term, count(*) as count
from companies, business_types, business_types_to_companies
where
business_types.id = business_types_to_companies.term_id
and companies.id = business_types_to_companies.company_id
and raw_score > 25
and diversity = 1
and company_id in (
select parent_id
from duns_match_sealed_air_072815
group by duns_number
)
group by term
order by count desc;
What is going on?
It's down to the way it processes the query - I believe it has to run your embedded query once for each row, whereas using two queries allows you to store the result.
Hope this helps!
The query has been re-written using JOIN, but particularly I've used EXISTS instead of IN. This is a short in the dark. It is possible that there may be many values generated in the sub-query causing the outer query to struggle while it goes through matching each item returned from the sub-query.
select term, count(*) as count
from companies c
inner join business_types_to_companies bc on bc.company_id = c.id
inner join business_types b on b.id = bc.term_id
where
raw_score > 25
and diversity = 1
and exists (
select 1
from duns_match_sealed_air_072815
where parent_id = c.id
)
group by term
order by count desc;
First, with respect, your subquery doesn't use GROUP BY in a sensible way.
select parent_id /* wrong GROUP BY */
from duns_match_sealed_air_072815
group by duns_number
In fact, it misuses the pernicious MySQL extension to GROUP BY. Read this. http://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html . I can't tell what your application logic intends from this query, but I can tell you that it actually returns an unpredictably selected parent_id value associated with each distinct duns_number value.
Do you want
select MIN(parent_id) parent_id
from duns_match_sealed_air_072815
group by duns_number
or something like that? That one selects the lowest parent ID associated with each given number.
Sometimes MySQL has a hard time optimizing the WHERE .... IN () query pattern. Try a join instead. Like this:
select term, count(*) as count
from companies
join (
select MIN(parent_id) parent_id
from duns_match_sealed_air_072815
group by duns_number
) idlist ON companies.id = idlist.parent_id
join business_types_to_companies ON companies.id = business_types_to_companies.company_id
join business_types ON business_types.id = business_types_to_companies.term_id
where raw_score > 25
and diversity = 1
group by term
order by count desc
To optimize this further we'll need to see the table definitions and the output from EXPLAIN.

MySQL evaluate case with subquery

I am trying to create a custom sort that involves the count of some records in another table. For example, if one record has no records associated with it in the other table, it should appear higher in the sort than if it had one or more records. Here's what I have so far:
SELECT People.*, Organizations.Name AS Organization_Name,
(CASE
WHEN Sent IS NULL AND COUNT(SELECT * FROM Graphics WHERE People.Organization_ID = Graphics.Organization_ID) = 0 THEN 0
ELSE 1
END) AS Status
FROM People
LEFT JOIN Organizations ON Organizations.ID = People.Organization_ID
ORDER BY Status ASC
The subquery within the COUNT is not working. What is the correct way to do something like this?
Update: I moved the case statement into the order by clause and added a join:
SELECT People.*, Organizations.Name AS Organization_Name
FROM People
LEFT JOIN Organizations ON Organizations.ID = People.Organization_ID
LEFT JOIN Graphics ON Graphics.Organization_ID = People.Organization_ID
GROUP BY People.ID
ORDER BY
CASE
WHEN Sent IS NULL AND Graphics.ID IS NULL THEN 0
ELSE 1
END ASC
So if if the People record does not have any graphics, Graphics.ID will be null. This achieves the immediate need.
If what you tried does not work, it can be done by joining against a subquery, and placing the CASE expression into ORDER BY as well:
SELECT
People.*,
orgcount.num
FROM People JOIN (
SELECT Organization_ID, COUNT(*) AS num FROM Graphics GROUP BY Organization_ID
) orgcount ON People.Organization_ID = orgcount.num
ORDER BY
CASE WHEN Sent IS NULL AND orgcount.num = 0 THEN 0 ELSE 1 END,
orgcount.num DESC
You could use an outer join to the Graphics table to get the data needed for your sort.
Since I don't know your schema, I made an assumption that the People table has a primary key column called ID. If the PK column has a different name, you should substitute that in the GROUP BY clause.
Something like this should work for you:
SELECT People.*, (count(Distinct Graphics.Organization_ID) > 0) as Status
FROM People
LEFT OUTER JOIN Graphics ON People.Organization_ID = Graphics.Organization_ID
GROUP BY People.ID
ORDER BY Status ASC
Fairly straight forward with a LEFT JOIN provided you have some kind of primary key in the People table to GROUP on;
SELECT p.*, sent IS NOT NULL or COUNT(g.Organization_ID) Status
FROM People p LEFT JOIN Graphics g ON g.Organization_ID = p.Organization_ID
GROUP BY p.primary_key
ORDER BY Status
Demo here.

MySQL query performance problem - INNER JOIN, ORDER BY, DESC

I have got this query:
SELECT
t.type_id, t.product_id, u.account_id, t.name, u.username
FROM
types AS t
INNER JOIN
( SELECT user_id, username, account_id
FROM users WHERE account_id=$account_id ) AS u
ON
t.user_id = u.user_id
ORDER BY
t.type_id DESC
1st question:
It takes around 30seconds to do this at the moment with only 18k records in types table.
The only indexes at the moment are only a primary indexes with just id.
Would the long time be caused by a lack of more indexes? Or would it be more to do with the structure of this query?
2nd question:
How can I add the LIMIT so I only get 100 records with the highest type_id?
Without changing the results, I think it is a 100 times faster if you don't make a sub-select of your users table. It is not needed at all in this case.
You can just add LIMIT 100 to get only the first 100 results (or less if there aren't a 100).
SELECT SQL_CALC_FOUND_ROWS /* Calculate the total number of rows, without the LIMIT */
t.type_id, t.product_id, u.account_id, t.name, u.username
FROM
types t
INNER JOIN users u ON u.user_id = t.user_id
WHERE
u.account_id = $account_id
ORDER BY
t.type_id DESC
LIMIT 1
Then, execute a second query to get the total number of rows that is calculated.
SELECT FOUND_ROWS()
That sub select on MySQL is going to slow down your query. I'm assuming that this
SELECT user_id, username, account_id
FROM users WHERE account_id=$account_id
doesn't return many rows at all. If that's the case then the sub select alone won't explain the delay you're seeing.
Try throwing an index on user_id in your types table. Without it, you're doing a full table scan of 18k records for each record returned by that sub select.
Inner join the users table and add that index and I bet you see a huge increase in speed.