MySQL GROUP & COUNT Multiple tables - mysql

I have a 3 part problem thats been giving me trouble I know how to get the tables to work if I query 1 table
at a time but I can't seem to figure out how I can combine both the tags and more_tags tables to get the same results.
The 3 main problems I have are Listed below.
PROBLEM 1
I want to be able to group the same tag from both the tags and more_tags tables.
PROBLEM 2
I also want to be able to display the tags from each table that are not present in the other table.
PROBLEM 3
I also want to count the total amount of times the tag appears in both tags and more_tags tables.
MYSQL Tables
SELECT `tags`.`tag_id`, `tags`.`tag_name`, COUNT(`tags`.`tag_name`) as 'num'
FROM `tags`
INNER JOIN `users` ON `tags`.`user_id` = `users`.`user_id`
WHERE `users`.`active` IS NULL
GROUP BY `tags`.`tag_name`
ORDER BY `tags`.`tag_name` ASC";
SELECT `more_tags`.`tag_id`, `more_tags`.`tag_name`, COUNT(`more_tags`.`tag_name`) as 'num'
FROM `more_tags`
INNER JOIN `users` ON `more_tags`.`user_id` = `users`.`user_id`
WHERE `users`.`active` IS NULL
GROUP BY `more_tags`.`tag_name`
ORDER BY `more_tags`.`tag_name` ASC";
Desired output
tag_id tag_name num
10 apple 12
192 pear 1
197 bored 1
203 sad 3
207 ads 2
217 news 1
190 bf 1
196 cape 1

Problem 1:
SELECT tag_id, tag_name, count(*)
FROM (
SELECT tag_id, tag_name FROM tags
UNION ALL
SELECT tag_id, tag_name FROM more_tags
) s
GROUP BY tag_id, tag_name
Problem 2:
SELECT tag_id, tag_name, 'not present in more tags' as description
FROM tags LEFT JOIN more_tags ON tags.tag_id=more_tags.tag_id
WHERE more_tags.tag_id IS NULL
UNION ALL
SELECT tag_id, tag_name, 'not present in tags' as description
FROM tags RIGHT JOIN more_tags ON tags.tag_id=more_tags.tag_id
WHERE tags.tag_id IS NULL
Problem 3:
SELECT tag_id, tag_name, COUNT(*)
FROM tags INNER JOIN more_tags ON tags.tag_id=more_tags.tag_id
GROUP BY tag_id, tag_name

You can use UNION to add together the results sets from two queries iff those queries return rows with the same structure (that is, if the first column in the first query is an int, then the first column in the second query must be an int, and so on). Read all about it in http://dev.mysql.com/doc/refman/5.1/en/union.html
Once you've written the two selects and joined them with a UNION statement, you can use that as a subquery for GROUP BY or other things:
SELECT * FROM (
(SELECT 1 AS ticked, col1, col2 FROM table1 INNER JOIN table2 USING (col3))
UNION
(SELECT 0 AS ticked, col1, col2 FROM table1)
) AS combined_table /*derived tables need a unique name*/
GROUP BY col1 /*group by the unique col1 to stop duplicates*/
ORDER BY ticked DESC

Related

Need my table to create only one row per id

I need to join two tables, which are:
TABLE 1
t1_id | t1_text | t1_date
TABLE 2
t2_id | t2_text | t2_date | t1_id
What i want to get:
t1_text | t1_date | t2_text | t2_date
table has to show most recent, unique, t1 rows, together with their linked t2_text and its t2_date
This is what ive got so far:
SELECT `table_1`.`t1_text` AS 'Text', `table_1`.`t1_date` AS 't1_date', `table_2`.`t2_text` AS 't2_Text', `table_2`.`t2_date` AS 'Date'
FROM `table_1`
LEFT JOIN `table_2` ON `table_1`.`t1_id`=`table_2`.`t1_id`
ORDER BY `table_1`.`t1_date` DESC
LIMIT 10
Its close, but no cigar. I still get the same t1 rows to show up more than once
I am not sure I understand clearly what is your desired output. Try the following:
SELECT news.text AS 'News', news.date AS 'Publish date',
comments.text AS 'Most Recent Comment', comments.date AS 'Comment Date'
FROM news
JOIN (
SELECT text, MAX(newsId) as newsId, date
FROM comments
GROUP BY newsId
)comments ON news.newsId=comments.newsId
WHERE comments.text IS NOT NULL
ORDER BY news.date DESC LIMIT 10
With clarification regarding the uniqueness of the id fields:
select t1.t1_id, t1.t1_date, t1.t1_text, t2.t2_id, t2.t2_date, t2.t2_text
from table_1 t1
inner join (SELECT t1_id, max(t1_date) as date from table_1) t1m
on t1.t1_id = t1m.t1_id and t1.t1_date = t1m.date
inner join table_2 t2
on t1.t1_id = t2.t1_id
inner join (SELECT t2_id, max(t2_date) as date from table_2) t2m
on t2.t2_id = t2m.t2_id and t2.t2_date = t2m.date;
In this query the inline tables select the ids with the max date, which are then joined to the original table to get the remaining fields.
There still may be duplicate t1_id's however if the combination of t1_id and t1_date is not unique. But in that case I don't think that duplication should be hidden, but rather adressed.
Original answer:
You need to use an inner join rather than a left join.
SELECT `table_1`.`t1_text` AS 'Text', `table_1`.`t1_date` AS 't1_date', `table_2`.`t2_text` AS 't2_Text', `table_2`.`t2_date` AS 'Date'
FROM `table_1`
INNER JOIN `table_2`
ON `table_1`.`t1_id`=`table_2`.`t1_id`
ORDER BY `table_1`.`t1_date` DESC LIMIT 10
Also, some of your quote characters are not consistent, different databases may require different quote characters to delimit names, but the mix might give you problems.

How to join two tables, with distinct columns on either side?

I have two tables I'm trying to join to produce a unique set of data for a third table, but having trouble doing this properly.
The left table has an id field, as well as a common join field (a).
The right table has the common join field (a), and another distinct field (b)
I'm trying to extract a result-set of id and b, where neither id nor b are duplicated.
I have an SQL fiddle set up: http://www.sqlfiddle.com/#!9/208de/3/0
The ideal results should be:
id | b
---+---
1 | 1
2 | 2
3 | 3
Each id and b value appears only once (it's only coincidence they match here, that can't be assumed always).
Thanks
What about a CTE along with a DISTINCT, Would that work?
WITH
cte1 (ID, B)
AS
(
SELECT DISTINCT Table1.ID
FROM Table1
WHERE Table1.ID IS NOT NULL
GROUP BY Table1.ID
)
SELECT DISTINCT
Table2.b
FROM Table2 AS sp
INNER JOIN cte1 AS ts
ON sp.b <> ts.ID
ORDER BY ts.ID DESC

Query which should return the non existing values in table

I have
A list of Primary keys(around 10).
A table has 100 rows.
Lets say among 10 keys which I have, if there are 8 keys found in the table.
I need the output of the remaining 2 keys which is not present in the table.
Eg: i have 10 empl id's which i need to query in Empl table.
Empl table has 100 or even more rows. Among 10 empl id which i have , only 8 are there in empl table. I need to get that remaining 2 empl ids which are not there in empl table.
NOTE: if you use not in , it will give all other empl ids from empl table. But i need only those two which are not present.
To make the query a little shorter I give you an example for 3 keys you have to check in a table
select k.*
from
(
select 1 as pk
union all
select 3
union all
select 7
) k
left join your_table t on t.id = k.pk
where t.id is null
select * from table where my_key not in (select distinct my_key from the_other_table)
SELECT *
FROM Table10 T
WHERE not exists ( SELECT 1
FROM Table100 Q
WHERE T.ID = Q.ID )

Select Matched Pairs from Two Tables

I need to select matched pairs from two tables containing similarly structured data. "Matched Pair" here means two rows that reference each other in the 'match' column.
A single-table matched pair example:
TABLE
----
id | matchid
1 | 2
2 | 1
ID 1 and 2 are a matched pair because each has a match entry for the other.
Now the real question: what is the best (fastest) way to select the matched pairs that appear in both tables:
Table ONE (id, matchid)
Table TWO (id, matchid)
Example data:
ONE TWO
---- ----
id | matchid id | matchid
1 | 2 2 | 3
2 | 3 3 | 2
3 | 2
4 | 5
5 | 4
The desired result is a single row with IDs 2 and 3.
RESULT
----
id | id
2 | 3
This is because 2 & 3 are a matched pair in table ONE and in table TWO. 4 & 5 are a matched pair in table ONE but not TWO, so we don't select them. 1 and 2 are not a match pair at all since 2 does not have a matching entry for 1.
I can get the matched pairs from one table with this:
SELECT a.id, b.id
FROM ONE a JOIN ONE b
ON a.id = b.matchid AND a.matchid = b.id
WHERE a.id < b.id
How should I build a query that selects only the matching pairs that appear in both tables?
Should I:
Select the query above for each table and WHERE EXISTS them together?
Select the query above for each table and JOIN them together?
Select the query above then JOIN table TWO twice, once for 'id' and once for 'matchid'?
Select the query above for each table and loop through to compare them back in php?
Somehow filter table TWO down so we only have to look at the IDs in matched pairs in table ONE?
Do something totally different?
(Since this is a question of efficiency, it is worth noting that the matches will be quite sparse, maybe 1/1000 or less, and each table will have 100,000+ rows.)
I think I get your point. You want to filter the records in which the pairs exists on both tables.
SELECT LEAST(a.ID, a.MatchID) ID, GREATEST(a.ID, a.MatchID) MatchID
FROM One a
INNER JOIN Two b
ON a.ID = b.ID AND
a.matchID = b.matchID
GROUP BY LEAST(a.ID, a.MatchID), GREATEST(a.ID, a.MatchID)
HAVING COUNT(*) > 1
SQLFiddle Demo
Try this Query:
select
O.id,
O.matchid
from
ONE O
where
(CAST(O.id as CHAR(50))+'~'+CAST(O.matchid as CHAR(50)))
in (select CAST(T.id as CHAR(50))+'~'+CAST(T.matchid as CHAR(50)) from TWO T)
Edited Query:
select distinct
Least(O.id,O.matchid) ID,
Greatest(O.id,O.matchid) MatchID
from
ONE O
where
(CAST(O.id as CHAR(50))+'~'+CAST(O.matchid as CHAR(50)))
in (select CAST(T.id as CHAR(50))+'~'+CAST(T.matchid as CHAR(50)) from TWO T)
and (CAST(O.matchid as CHAR(50))+'~'+CAST(O.id as CHAR(50)))
in (select CAST(T.id as CHAR(50))+'~'+CAST(T.matchid as CHAR(50)) from TWO T)
SQL Fiddle
Naive version, which checks all the four rows that need to exist:
-- EXPLAIN ANALYZE
WITH both_one AS (
SELECT o.id, o.matchid
FROM one o
WHERE o.id < o.matchid
AND EXISTS ( SELECT * FROM one x WHERE x.id = o.matchid AND x.matchid = o.id)
)
, both_two AS (
SELECT t.id, t.matchid
FROM two t
WHERE t.id < t.matchid
AND EXISTS ( SELECT * FROM two x WHERE x.id = t.matchid AND x.matchid = t.id)
)
SELECT *
FROM both_one oo
WHERE EXISTS (
SELECT *
FROM both_two tt
WHERE tt.id = oo.id AND tt.matchid = oo.matchid
);
This one is simpler :
-- EXPLAIN ANALYZE
WITH pair AS (
SELECT o.id, o.matchid
FROM one o
WHERE EXISTS ( SELECT * FROM two x WHERE x.id = o.id AND x.matchid = o.matchid)
)
SELECT *
FROM pair pp
WHERE EXISTS (
SELECT *
FROM pair xx
WHERE xx.id = pp.matchid AND xx.matchid = pp.id
)
AND pp.id < pp.matchid
;

MySQL id merge question

Sorry for the vague topic but I'm having a hard time explaining this. What I'm trying to do is fetching an ID representing a post which can be posted in different categories, and I want my post to belong to all three categories to match the criteria. The table looks like this
id category_id
1 3
1 4
1 8
What I wanna do is fetch an id that belongs to all 3 of these categories, but since they're on different rows I can't use
SELECT id FROM table WHERE category_id = '3' AND category_id = '4' AND category_id = '8';
This will of course return nothing at all, since no row matches that criteria. I've also tried with the WHERE IN clause
SELECT id from table WHERE category_id IN (3, 4, 8);
This returns any post in any of these categories, the post I want returned has to be in all three of these categories.
So the question becomes, is there any good way to look for an id that belongs to all three of these categories, or do I have to use the WHERE IN clause and see if I get 3 rows with the id 1, then I'll know that it occured three times, therefor belongs to all three categories, seems like a bad solution.
Thanks in advance. I appreciate the help. :)
You could use group_concat to get a comma separated string of your category id's and check if that contains all the categories you're filtering
SELECT id, GROUP_CONCAT(t2.category_id) as categories
FROM table AS t1
INNER JOIN table AS t2 ON t1.id = t2.id
WHERE
FIND_IN_SET('3', categories) IS NOT NULL AND
FIND_IN_SET('4', categories) IS NOT NULL AND
FIND_IN_SET('8', categories) IS NOT NULL
Update
SELECT t1.id, GROUP_CONCAT(t2.category_id) as `categories`
FROM `table` AS t1
INNER JOIN `table` AS t2 ON t1.id = t2.id
HAVING
FIND_IN_SET('3', `categories`) IS NOT NULL AND
FIND_IN_SET('4', `categories`) IS NOT NULL AND
FIND_IN_SET('8', `categories`) IS NOT NULL
The last query would not have worked, since it is a group function the value cannot be used in the WHERE clause but can be used in the HAVING clause.
I think you need to say GROUP BY id like this:
SELECT id FROM table WHERE category_id = '3' OR category_id = '4' OR category_id = '8'; GROUP BY id;
Hope that works for you.
i believe you need to establish the amount of categories a post appears in.
this can be done by applying COUNT and HAVING to the SQL query as follows:
SELECT id,
COUNT(category_id) AS categories
FROM `table`
GROUP BY id
HAVING categories > 3
and if you wish the number of categories in your site to change dynamically you can always have an inner SELECT statement like so:
SELECT id,
COUNT(category_id) AS categories
FROM `table`
GROUP BY id
HAVING categories > (
SELECT COUNT(category_id)
FROM `categories`
)
where categories is the table you store all your categories information