Running an EXPLAIN on some of my query tests have resulted in slow ALL joins even with indexes.
How do I make a MYSQL query with the following information more efficient?
Tables
counter: id (pk), timestamp, user_id (fk)
user: id (PK), username, website_id (fk)
website: id (pk), sitename
SELECT t2.username, t3.sitename, count(*) as views FROM counter t1
LEFT JOIN user t2 ON t2.id = t1.user_id
LEFT JOIN website t3 ON t3.id = t2.website_id
WHERE t1.id <> ""
GROUP BY t1.id
ORDER BY t1.id DESC
The result in an html table:
username, sitename, counter_views
Don't count(*), instead use count(t1.id)
Question Edit Change
With the question edit, you should put a column name in the count statement of what you want to count, e.g. count(user.id)
I believe the sql is wrong. Don't you want this:
select u.username, s.sitename, count(c.id)
from user u
join website s on u.website_id = s.id
join counter c on u.id = c.user_id
where u.id <> ""
group by u.username, s.sitename
order by u.id desc
Is your data populated yet? If these tables are empty, or very small, the optimizer may be choosing an 'all' query because the whole table is on one page. One page load from disk to get the whole table is faster than hitting a page on disk for the index, and then another page for the real data page.
Your query looks wrong. I would use something like this:
SELECT u.username, w.sitename, c.views
FROM (
SELECT user_id, COUNT(*) AS views FROM counter GROUP BY user_id
) AS c
LEFT JOIN user u ON u.id = c.user_id
LEFT JOIN website w ON w.id = u.website_id
ORDER BY c.views DESC
I would add index for counter.user_id too.
Related
Just want to know can I make a such query?
SELECT
t1.id,
t1.name
FROM t1
LEFT JOIN t2 ON t2.id = (SELECT id FROM t3 WHERE t1.address LIKE 'street%' ORDER BY name ASC)
ORDER BY name DESC
This is because I want to add more complex query to the LEFT JOIN with sorting and order and some statements which depends on another table..
Thanks!
U.P.D.
SELECT * FROM t3 WHERE t1.address LIKE CONCAT(address,'%') ORDER BY LENGTH(address) DESC
Actually, I want to write this query as LEFT JOIN subquery (ORDER BY in case of LIKE does make sense!).
Theoretically you can do this.
Writing subquery in join statement will have no effect other than filtering the cartesian product of the two tables just like the where condition. But writing query this way makes no sense as we don't know the context in which you are using it.
The above query can be written in much cleaner way as follows :
SELECT
t1.id,
t1.name
FROM t1, t2
WHERE t2.id in (SELECT id FROM t3 WHERE address LIKE 'street%')
ORDER BY name DESC
It will produce the same result set as the query you provided
I don't think this will be too complicated to explain, but certainly complicated to get it working.
First of all, I have a couple of tables regarding users comments, one table for each section (forum, articles etc), as shown below:
site_users (id, username, ...) [Table that holds user's info]
site_articles_comments (id, user_id, comment, ...) [Where user_id = site_users.id]
site_forum_comments (id, user_id, comment, ...) [Same for site_articles_comments]
The thing is that every new row is a new comment and users can comment multiple times, which means that more rows are being added, thus making the need of sorting the number of rows to get the amount of comments in some sort of ranking system.
I was able to make a simple forum rank by doing this simple query:
SELECT u.id, u.username, COUNT(r.id) AS rank FROM site_users AS u LEFT
JOIN site_forum_comments AS r ON u.id = r.user_id GROUP BY u.username,
u.id ORDER BY rank DESC LIMIT :l
This query sorts all users from the database, where the user who has commented the most is always on top.
What I need, in the other hand, is to have a global ranking system, which sums the amount of comments in each section (articles, forum etc) and displays the users accordingly.
I was playing around with the sql to do that and the last thing I came up with was this huge query:
SELECT u.id, u.username, (COUNT(a.id) + COUNT(f.id)) AS rank FROM
site_users u LEFT JOIN site_articles_comments a ON a.user_id = u.id
LEFT JOIN site_forum_comments f ON f.user_id = u.id GROUP BY
u.username, u.id ORDER BY rank DESC LIMIT :l
This, however, returns null. What could I possibly do to achieve the result I want?
Thanks in advance,
Mateus
EDIT1: Sorry for the lack of information, this is regarding MySQL.
The problem is math with nulls, and ordering with nulls (check into the "NULLS LAST" option for overriding the default ordering which returns the nulls first for a descending order).
In your case, with the outer joins, if the user has a ton of article comments but no forum comments, well, 100 + null = null in Oracle math. So to get the math to work you need to make null=0. That's where NVL() comes in (and also has the nice side-effect of eliminating pesky nulls from your result set)!
SELECT u.id, u.username, (NVL(COUNT(a.id),0) + NVL(COUNT(f.id),0)) AS rank
FROM site_users u
LEFT JOIN site_articles_comments a ON a.user_id = u.id
LEFT JOIN site_forum_comments f ON f.user_id = u.id
GROUP BY u.username, u.id ORDER BY rank DESC LIMIT :l
I see you have both MySQL and Oracle in your tags - the above is for Oracle. If for MYSQL use COALESCE(COUNT(),0) instead.
try SELECT u.id, MIN(u.username) AS username, (COALESCE(COUNT(DISTINCT(a.id)),0) + COALESCE(COUNT(DISTINCT(f.id)),0)) AS rank
FROM site_users AS u
LEFT JOIN site_articles_comments AS a ON (a.user_id = u.id)
LEFT JOIN site_forum_comments AS f ON (f.user_id = u.id)
GROUP BY u.id
ORDER BY rank DESC
LIMIT :l
I'm doing some maintenance work on a database application and I've discovered that, joy of joys, even though values from one table are being used in the style of foreign keys, there's no foreign key constraints on the tables.
I'm trying to add FK constraints on these columns, but I'm finding that, because there's already a whole load of bad data in the tables from previous errors which have been naively corrected, I need to find the rows which don't match up to the other table and then delete them.
I've found some examples of this kind of query on the web, but they all seem to provide examples rather than explanations, and I don't understand why they work.
Can someone explain to me how to construct a query which returns all the rows with no matches in another table, and what it's doing, so that I can make these queries myself, rather than coming running to SO for every table in this mess that has no FK constraints?
Here's a simple query:
SELECT t1.ID
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL
The key points are:
LEFT JOIN is used; this will return ALL rows from Table1, regardless of whether or not there is a matching row in Table2.
The WHERE t2.ID IS NULL clause; this will restrict the results returned to only those rows where the ID returned from Table2 is null - in other words there is NO record in Table2 for that particular ID from Table1. Table2.ID will be returned as NULL for all records from Table1 where the ID is not matched in Table2.
I would use EXISTS expression since it is more powerful, you can e.g. more precisely choose rows you would like to join. In the case of LEFT JOIN, you have to take everything that's in the joined table. Its efficiency is probably the same as in the case of LEFT JOIN with null constraint.
SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)
Table 1 has a column that you want to add the foreign key constraint to, but the values in the foreign_key_id_column don't all match up with an id in table 2.
The initial select lists the ids from table1. These will be the rows we want to delete.
The NOT IN clause in the where statement limits the query to only rows where the value in the foreign_key_id_column is not in the list of table 2 ids.
The SELECT statement in parenthesis will get a list of all the ids that are in table 2.
Let we have the following 2 tables(salary and employee)
Now i want those records from employee table which are not in salary.
We can do this in 3 ways:
Using inner Join
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)
Using Left outer join
select * from employee e
left outer join salary s on e.id=s.id where s.id is null
Using Full Join
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)
Where T2 is the table to which you're adding the constraint:
SELECT *
FROM T2
WHERE constrained_field NOT
IN (
SELECT DISTINCT t.constrained_field
FROM T2
INNER JOIN T1 t
USING ( constrained_field )
)
And delete the results.
From similar question here MySQL Inner Join Query To Get Records Not Present in Other Table I got this to work
SELECT * FROM bigtable
LEFT JOIN smalltable ON bigtable.id = smalltable.id
WHERE smalltable.id IS NULL
smalltable is where you have missing records, bigtable is where you have all the records. The query list all the records that not exist in smalltable but exists on the bigtable. You could replace id by any other matching criteria.
I Dont Knew Which one Is Optimized (compared to #AdaTheDev
) but This one seems to be quicker when I use (atleast for me)
SELECT id FROM table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2
If You want to get any other specific attribute you can use:
SELECT COUNT(*) FROM table_1 where id in (SELECT id FROM table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);
You could opt for Views as shown below:
CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid,
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role
FROM authorizeduser as t1, project as p
and then work on the view for selecting or updating:
select * from AuthorizedUserProjectView where projectid = 49
which yields the result as shown in the picture below i.e. for non-matching column null has been filled in.
[Result of select on the view][1]
You can do something like this
SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName
FROM `products` left join `price` ON
price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId
IS NULL) WHERE Status="Active" AND Delete="No"
SELECT * FROM First_table
MINUS
SELECT * FROM another
How to select rows with no matching entry in Both table?
select * from [dbo].[EmppDetails] e
right join [Employee].[Gender] d on e.Gid=d.Gid
where e.Gid is Null
union
select * from [dbo].[EmppDetails] e
left join [Employee].[Gender] d on e.Gid=d.Gid
where d.Gid is Null
Creating a forum-type site and I have 3 tables, one for storing user information (users), one for the original post (threads), and one for an upvote system like SO has (votes).
The votes table has 3 columns, id, userid, and threadid. When a user upvotes a thread, a record is inserted into the votes table. When I query for the thread I want to know if the user has upvoted for it, essentially if a record exists in the votes table with the correct userid and threadid. I can do this in two queries, but I think there has to be a way to get everything in one.
My query currently:
"SELECT t.id, t.title, t.content u.id AS uid, u.username
FROM threads t, users u
WHERE t.id = '".$userid."'
AND t.author = '".$userid."'"
In case you need a better idea, the following will query the desired results ONLY if the user has upvoted. I need the query to still return if the record in the votes table doesn't exist (possibly return a vote value as null?).
"SELECT t.id, t.title, t.content u.id
AS uid, u.username v.id
FROM threads t, users u, votes v
WHERE t.id = '".$threadid."'
AND t.author = '".$userid."'
AND v.threadid = t.id
AND v.userid = '".$userid."'"
Also I taught myself (and am still learning) mysql and database design so if there's a better method/approach such as joining tables, please let me know. Thanks.
Your second query is doing inner joins, which would only return records that appear on both sides of the join. You'd want to do a left/right outer join on the votes table instead, so that you'd still get user+thread records even if there's no matching vote record.
SELECT t.id, t.title, t.content, u.id, u.username, v.id
FROM threads AS t
INNER JOIN users AS u ON t.userid = u.id
LEFT JOIN votes AS v ON (v.userid = u.id and t.id = v.threadid)
WHERE (u.id = $userid) AND (t.id = $threadid)
just guessing at this, but should be enough to get you started.
So I've been looking through how to get this to work and haven't been able to find it, or really even properly search.
I'm looking to join t1 to t2 where I get ALL results from t1, and it's only joined where t2's column value is "something", the table is set up that there are many items in the t2 column. So if t2's column doesn't equal "something", then just have the other data (but mainly I only want ONE of each value for t1 row, because there will be a max of one t2 row that qualifies per t1 row).
select * t1 LEFT JOIN t2 where t2.column="something" AND t1 conditions.
Any help would be appreciated, the tables are WordPress tables, and the thing I'm asked to do would be easier done without WordPress knowing.
--
Actual code attempt:
$ SELECT * FROM posts LEFT JOIN postmeta ON post_id = id WHERE post_status='publish' AND post_type='portfolio' AND meta_key='rjmeta_video'
$Table 1
$ID Title ....
$----------------------
$5 Some post I need outside of WP
$Table 2
$meta_id post_id meta_key ....
$--------------------------------
$3 5 rjmeta_video
$4 5 _edit_lock
$5 5 _edit_last
I believe this is what you mean:
select distinct t1.* from t1
LEFT JOIN t2 on [condition for join] and t2.column="something"
where [t1 conditions].
This assumes that you are only interested in the t1 fields.
What we don't know:
table schemas
how are they joined? t2.t1_id = t1.id ?
t1 conditions
DISTINCT used above will eliminate duplicate rows if there are multiple matches for the join
EDIT (OP comment response):
SELECT distinct posts.* FROM posts
LEFT JOIN postmeta ON (post_id = id and meta_key='rjmeta_video')
WHERE post_status='publish' AND post_type='portfolio'
This assumes that 'rjmeta_video' is the "something" you were referring to originally