Is it good if i write query like this:- (see query in where condition)
SELECT distinct(id) "idea_id"
FROM ideas
WHERE deleted_by_user = 0 AND moderation_flag = 1 AND
user_id in (select id
from users
where confirm like "yes")
ORDER BY time_of_creation DESC
let me know if there is some issue in this query :
thanx in advance..
You can wirte this query in two ways:
SELECT DISTINCT(i.id) "idea_id"
FROM ideas i
INNER JOIN users u ON i.user_id = u.id
WHERE i.deleted_by_user = 0 AND i.moderation_flag = 1 AND u.confirm = 'yes'
ORDER BY i.time_of_creation DESC;
And
SELECT DISTINCT(i.id) "idea_id"
FROM ideas i
WHERE i.deleted_by_user = 0 AND i.moderation_flag = 1 AND
EXISTS (SELECT * FROM users u WHERE i.user_id = u.id AND u.confirm = 'yes')
ORDER BY i.time_of_creation DESC;
SELECT distinct a.ID idea_id
FROM ideas a
INNER JOIN users b
ON a.user_id = b.id
WHERE a.deleted_by_user = 0 AND
a.moderation_flag = 1
b.confirm = 'YES'
ORDER BY time_of_creation DESC
To answer your question - there are no problems with using subqueries.
On the other hand, you have (at least) three different things to think about when writing a query in one way or another:
How efficient will the data base run my query? (If the data base is small, this may not matter at all)
How easy is this to formulate and write? - which often connects to
How easy is this to understand for someone else who reads my code? (and I may myself count as "somebody else" if I look into code I've written a year ago...)
If you have a database of a size where efficiency counts, the best way to select how to formulate a query is normally to write it in different ways and test it on the data base. (but often the query optimizer in the data base is so good, it does not matter)
SELECT distinct i.id "idea_id"
FROM ideas i join users u
on i.user_id=u.id and u.confirm ='yes'
WHERE i.deleted_by_user = 0
AND i.moderation_flag = 1
ORDER BY i.time_of_creation DESC
Related
I'm really struggling with this query and I hope somebody can help.
I am querying across multiple tables to get the dataset that I require. The following query is an anonymised version:
SELECT main_table.id,
sub_table_1.field_1,
main_table.field_1,
main_table.field_2,
main_table.field_3,
main_table.field_4,
main_table.field_5,
main_table.field_6,
main_table.field_7,
sub_table_2.field_1,
sub_table_2.field_2,
sub_table_2.field_3,
sub_table_3.field_1,
sub_table_4.field_1,
sub_table_4.field_2
FROM main_table
INNER JOIN sub_table_4 ON sub_table_4.id = main_table.id
INNER JOIN sub_table_2 ON sub_table_2.id = main_table.id
INNER JOIN sub_table_3 ON sub_table_3.id = main_table.id
INNER JOIN sub_table_1 ON sub_table_1.id = main_table.id
WHERE sub_table_4.field_1 = '' AND sub_table_4.field_2 = '0' AND sub_table_2.field_1 != ''
The query works, the problem I have is sub_table_1 has a revision number (int 11). Currently I get duplicate records with different revision numbers and different versions of sub_table_1.field_1 which is to be expected, but I want to limit the result set to only include results limited by the latest revision number, giving me only the latest sub_table_1_field_1 and I really can not figure it out!
Can anybody lend me a hand?
Many Thanks.
It's always important to remember that a JOIN can be on a subquery as well as a table. You could build a subquery that returns the results you want to see then, once you've got the data you want, join it in the parent query.
It's hard to 'tailor' an answer that's specific to you problem, as it's too obfuscated (as you admit) to know what the data and tables really look like, but as an example:
Say table1 has four fields: id, revision_no, name and stuff. You want to return a distinct list of name values, with their latest version of stuff (which, we'll pretend varies by revision). You could do this in isolation as:
select t.* from table1 t
inner join
(SELECT name, max(revision_no) maxr
FROM table1
GROUP BY name) mx
on mx.name = t.name
and mx.maxr = t.revision_no;
(Note: see fiddle at the end)
That would return each individual name with the latest revision of stuff.
Once you've got that nailed down, you could then swap out
INNER JOIN sub_table_1 ON sub_table_1.id = main_table.id
....with....
INNER JOIN (select t.* from table1 t
inner join
(SELECT name, max(revision_no) maxr
FROM table1
GROUP BY name) mx
on mx.name = t.name
and mx.maxr = t.revision_no) sub_table_1
ON sub_table_1.id = main_table.id
...which would allow a join with a recordset that is more tailored to that which you want to join (again, don't get hung up on the actual query I've used, it's just there to demonstrate the method).
There may well be more elegant ways to achieve this, but it's sometimes good to start with a simple solution that's easier to replicate, then simplify it once you've got the general understanding of the what and why nailed down.
Hope that helps - as I say, it's as specific as I could offer without having an idea of the real data you're using.
(for the sake of reference, here is a fiddle with a working version of the above example query)
In your case where you only need one column from the table, make this a subquery in your select clause instead of than a join. You get the latest revision by ordering by revision number descending and limiting the result to one row.
SELECT
main_table.id,
(
select sub_table_1.field_1
from sub_table_1
where sub_table_1.id = main_table.id
order by revision_number desc
limit 1
) as sub_table_1_field_1,
main_table.field_1,
...
FROM main_table
INNER JOIN sub_table_4 ON sub_table_4.id = main_table.id
INNER JOIN sub_table_2 ON sub_table_2.id = main_table.id
INNER JOIN sub_table_3 ON sub_table_3.id = main_table.id
WHERE sub_table_4.field_1 = ''
AND sub_table_4.field_2 = '0'
AND sub_table_2.field_1 != '';
I'm struggling to make a query efficient enough. I'm using Doctrine2 ORM (the query is build with QueryBuilder) and part of my query is running very slow - takes about 4s with table of 5000 rows.
This is the relevant part of db schema:
TABLE user
id (primary)
... (plenty of rows, not relevant to the query)
TABLE slot
id (primary)
user_id (foreign for user)
date (datetime)
And this is how my query looks like (it's the basic version, there's a lot of filters to be applied, but these work like fine for now)
SELECT
u.id AS uid,
COUNT(DISTINCT s_order.id) AS sclr_1,
COUNT(DISTINCT s_filter.id) AS sclr_2
FROM
user u
LEFT JOIN slot s_order ON (s_order.user_id = u.id)
LEFT JOIN slot s_filter ON (s_filter.user_id = u.id)
WHERE
(
(
(
s_order.date BETWEEN ?
AND ?
)
AND (
s_filter.date BETWEEN ?
AND ?
)
)
AND (u.deleted_at IS NULL)
)
AND u.userType IN ('2')
GROUP BY
u.id
HAVING
sclr_2 > 0
ORDER BY
sclr_1 DESC
LIMIT
12
Let me explain what I'm trying to achieve here:
I need to filter users who has any slots between 1 week ago and 1 week ahead, then order them by count of slots available between now and 1 week ahead. The part of query causing issues is LEFT JOIN of s_filter and I'm wondering whether perhaps there's a way to improve the performance of that query?
Any help appreciated really, even if it's only plain SQL I'll try to convert it to DQL myself!
#UPDATE
Just an additional info that I forgot, the LIMIT in query is for pagination purposes!
#UPDATE 2
After a while of tweaking the query I figured out that I can use JOIN for filtering instead of LEFT JOIN + COUNT, so my query does look like that now:
SELECT
u.id AS uid, COUNT(DISTINCT s_order.id) AS ordinal
FROM
langu_user u
LEFT JOIN
slot s_order ON (s_order.user_id = u.id) AND s_order.date BETWEEN '2017-02-03 14:03:22' AND '2017-02-10 14:03:22'
JOIN
slot s_filter ON (s_filter.user_id = u.id) AND s_filter.date BETWEEN '2017-01-27 14:03:22' AND '2017-02-10 14:03:22'
WHERE
u.deleted_at IS NULL
AND u.userType IN ('2')
GROUP BY u.id
ORDER BY ordinal DESC
LIMIT 12
And it went down from 4.1-4.3s to 3.6~
This question is continue of this here, since they don't want to answer without opening new.
This is my tables:
customer_profiles
friend_levels
so I have a query that get what level he's from, so when he got points = 168 then he will getGreat Friend so this is the SQL that I've already have.
SELECT s.*
FROM customer_profiles t
INNER JOIN friend_levels s ON(t.friend_points >= s.points_needed)
WHERE s.points_needed = (SELECT max(f.points_needed)
FROM friend_levels f
WHERE t.friend_points >= f.points_needed)
AND t.user_id = $user_id
and result like this
so my question is how to get the next value of it in order to use it like this? For ex. If I am on the Great Friend level, then I have to get the Best Friend level.
This should be simple:
SELECT (SELECT min(fl.points_needed)
FROM friend_levels fl
WHERE fl.points_needed > cp.friend_points) - cp.friend_points AS points_needed_for_next_level
FROM customer_profiles cp
WHERE cp.user_id = $user_id;
In the following query, I show the latest status of the sale (by stage, in this case the number 3). The query is based on a subquery in the status history of the sale:
SELECT v.id_sale,
IFNULL((
SELECT (CASE WHEN IFNULL( vec.description, '' ) = ''
THEN ve.name
ELSE vec.description
END)
FROM t_record veh
INNER JOIN t_state_campaign vec ON vec.id_state_campaign = veh.id_state_campaign
INNER JOIN t_state ve ON ve.id_state = vec.id_state
WHERE veh.id_sale = v.id_sale
AND vec.id_stage = 3
ORDER BY veh.id_record DESC
LIMIT 1
), 'x') sale_state_3
FROM t_sale v
INNER JOIN t_quarters sd ON v.id_quarters = sd.id_quarters
WHERE 1 =1
AND v.flag =1
AND v.id_quarters =4
AND EXISTS (
SELECT '1'
FROM t_record
WHERE id_sale = v.id_sale
LIMIT 1
)
the query delay 0.0057seg and show 1011 records.
Because I have to filter the sales by the name of the state as it would have to repeat the subquery in a where clause, I have decided to change the same query using joins. In this case, I'm using the MAX function to obtain the latest status:
SELECT
v.id_sale,
IFNULL(veh3.State3,'x') AS sale_state_3
FROM t_sale v
INNER JOIN t_quarters sd ON v.id_quarters = sd.id_quarters
LEFT JOIN (
SELECT veh.id_sale,
(CASE WHEN IFNULL(vec.description,'') = ''
THEN ve.name
ELSE vec.description END) AS State3
FROM t_record veh
INNER JOIN (
SELECT id_sale, MAX(id_record) AS max_rating
FROM(
SELECT veh.id_sale, id_record
FROM t_record veh
INNER JOIN t_state_campaign vec ON vec.id_state_campaign = veh.id_state_campaign AND vec.id_stage = 3
) m
GROUP BY id_sale
) x ON x.max_rating = veh.id_record
INNER JOIN t_state_campaign vec ON vec.id_state_campaign = veh.id_state_campaign
INNER JOIN t_state ve ON ve.id_state = vec.id_state
) veh3 ON veh3.id_sale = v.id_sale
WHERE v.flag = 1
AND v.id_quarters = 4
This query shows the same results (1011). But the problem is it takes 0.0753 sec
Reviewing the possibilities I have found the factor that makes the difference in the speed of the query:
AND EXISTS (
SELECT '1'
FROM t_record
WHERE id_sale = v.id_sale
LIMIT 1
)
If I remove this clause, both queries the same time delay... Why it works better? Is there any way to use this clause in the joins? I hope your help.
EDIT
I will show the results of EXPLAIN for each query respectively:
q1:
q2:
Interesting, so that little statement basically determines if there is a match between t_record.id_sale and t_sale.id_sale.
Why is this making your query run faster? Because Where statements applied prior to subSelects in the select statement, so if there is no record to go with the sale, then it doesn't bother processing the subSelect. Which is netting you some time. So that's why it works better.
Is it going to work in your join syntax? I don't really know without having your tables to test against but you can always just apply it to the end and find out. Add the keyword EXPLAIN to the beginning of your query and you will get a plan of execution which will help you optimize things. Probably the best way to get better results in your join syntax is to add some indexes to your tables.
But I ask you, is this even necessary? You have a query returning in <8 hundredths of a second. Unless this query is getting ran thousands of times an hour, this is not really taxing your DB at all and your time is probably better spent making improvements elsewhere in your application.
It seems I'm getting an "impossible WHERE" on a SELECT query. I've posted two queries below, which differentiate in the subquery. What these queries do is check to see if a user has saved something once before, before updating a count. I am using SELECT for testing purposes, but the actual query would be using UPDATE:
UPDATE articles SET article_count = article_count+1
WHERE id = 2343243 AND (
SELECT COUNT(*)
FROM posts as p
WHERE p.post_id = 2343243 AND p.user_id = 3
) = 1;
The following two queries are what I'm using to test to see if the data is in the table (for testing only):
EXPLAIN
SELECT a.id
FROM articles as a
WHERE a.id = 2343243 AND (
SELECT COUNT(*)
FROM posts as p
WHERE p.post_id = a.id AND p.user_id = 3
) = 1;
Query 1 returns Impossible WHERE in EXPLAIN.
The select_type of the query #2 is SUBQUERY.
EXPLAIN
SELECT a.id
FROM articles as a
WHERE a.id = 2343243 AND (
SELECT COUNT(*)
FROM posts as p
WHERE p.post_id = 2343243 AND p.user_id = 3
) = 1;
Query 2 returns Impossible WHERE noticed after reading const tables in EXPLAIN.
The select_type of the Query 2 is DEPENDENT SUBQUERY.
Question: Any ideas on how to make this not an impossible WHERE query? And also, which would be faster?
After reading on Mysql.com about Impossible WHERE, it isn't really a good idea to work with such a constraint as WHERE 1 = 1 in a query:
Because it is never possible for this condition to be true, the
EXPLAIN statement will show the words Impossible WHERE. Informally, we
at MySQL say that the WHERE has been optimized away.
Mysql.com: http://dev.mysql.com/doc/internals/en/optimizer-eliminating-dead-code.html
I've decided to ditch that query for a new query I've written that utilizes indexes for both the main query and the subquery which is a faster solution and seemingly less complex:
UPDATE articles
SET article_count = IF (
(SELECT count(*) FROM posts WHERE post_id = 2343243 AND user_id = 3) = 1, article_count+1, article_count)
WHERE id = 2343243;
a prepared statement that doesn't use an actual constant might help (if mysql is using the actual constant that you are testing to declare impossibility based on the data)