I have two tables that store comments from two different users
Table1
id_table1 | comment | id_user | id_post
Table2
id_table2 | comment | id_someOtherUser | id_post
I would really like to make only one table out of this two because comments are posted on the same post, but i can't because i have two different id's for two different kind of users.
My question is how can i list all posts DESC by number of comments combined from two tables?
If i do something like
SELECT P.*, count(*) as count from Table1 AS T1
LEFT JOIN post AS P ON T1.id_post = P.id_post GROUP BY P.id_post ORDER BY count DESC
Then i have posts for table1, same can be done for table2, but how can i combine comments from both tables for the same post?
I would use UNION ALL to combine the two comment tables in a common format, then do the JOIN:
SELECT P.*, TC.count
FROM (
SELECT Ts.id_post, count(*) AS count
FROM (
SELECT id_post FROM Table1
UNION ALL
SELECT id_post FROM Table2
) AS Ts
GROUP BY Ts.id_post
) AS TC
LEFT JOIN post AS P ON TC.id_post = P.id_post
GROUP BY P.id_post
ORDER BY TC.count DESC
One way to solve this is to do separate counts on each table, then do a full outer join, and take the sum of each of the counts:
SELECT id_post, (count1 + count2) AS total_count FROM
(SELECT id_post, count(*) as count1 from Table1 AS T1
FULL OUTER JOIN
(SELECT id_post, count(*) as count2 from Table2 AS T2)
USING(id_post))
ORDER BY total_count DESC
Related
I'm new to MySQL, and I'd like some help in setting up a MySQL query to pull some data from a few tables (~100,000 rows) in a particular output format.
This problem involves three SQL tables:
allusers : This one contains user information. The columns of interest are userid and vip
table1 and table2 contain data, but they also have a userid column, which matches the userid column in allusers.
What I'd like to do:
I'd like to create a query which searches through allusers, finds the userid of those that are VIP, and then count the number of records in each of table1 and table2 grouped by the userid. So, my desired output is:
userid | Count in Table1 | Count in Table2
1 | 5 | 21
5 | 16 | 31
8 | 21 | 12
What I've done so far:
I've created this statement:
SELECT userid, count(1)
FROM table1
WHERE userid IN (SELECT userid FROM allusers WHERE vip IS NOT NULL)
GROUP BY userid
This gets me close to what I want. But now, I want to add another column with the respective counts from table2
I also tried using joins like this:
select A.userid, count(T1.userid), count(T2.userid) from allusers A
left join table1 T1 on T1.userid = A.userid
left join table2 T2 on T2.userid = A.userid
where A.vip is not null
group by A.userid
However, this query took a very long time and I had to kill the query. I'm assuming this is because using Joins for such large tables is very inefficient.
Similar Questions
This one is looking for a similar result as I am, but doesn't need nearly as much filtering with subqueries
This one sums up the counts across tables, while I need the counts separated into columns
Could someone help me set up the query to generate the data I need?
Thanks!
You need to pre-aggregate first, then join, otherwise the results will not be what you expect if a user has several rows in both table1 and table2. Besides, pre-aggregation is usually more efficient than outer aggregation in a situation such as yours.
Consider:
select a.userid, t1.cnt cnt1, t2.cnt cnt2
from allusers a
left join (select userid, count(*) cnt from table1 group by userid) t1
on t1.userid = a.userid
left join (select userid, count(*) cnt from table2 group by userid) t2
on t2.userid = a.userid
where a.vip is not null
This is a case where I would recommend correlated subqueries:
select a.userid,
(select count(*) from table1 t1 where t1.userid = a.userid) as cnt1,
(select count(*) from table2 t2 where t2.userid = a.userid) as cnt2
from allusers a
where a.vip is not null;
The reason that I recommend this approach is because you are filtering the alllusers table. That means that the pre-aggregation approach may be doing additional, unnecessary work.
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.
I would like to select data from a table like this (the table name is conversations_users) :
I would like to be able to retrieve a conversation ID that includes only two users. As instance, if I search a conversation specific to users 1 and 3 the conversation number 6 should be the unique result, because the conversation 5 also includes user 2.
I have tried to perform a request like
SELECT * FROM conversations_users AS table1 JOIN
conversations_users AS table2 ON
table1.conversation_ID = table2.conversationID
WHERE table1.userID = 3 AND
table2.userID = 1
But it returns both conversations 5 and 6. How can I fix that ?
Thank you in advance,
Pierre
Add the ON clause:
SELECT * FROM conversations_users AS table1 JOIN
conversations_users AS table2
ON table1.conversation_ID = table2.conversation_ID
WHERE table1.userID = 3 AND
table2.userID = 1
Update:
To get only coversations, where only 1 and 3 are involved, you can use having clause:
SELECT table1.conversation_ID FROM conversations_users AS table1 JOIN
conversations_users AS table2
ON table1.conversation_ID = table2.conversation_ID
WHERE table1.userID = 3 AND
table2.userID = 1
Group by table1.conversation_ID
having Count(*) = 2
The query you need looks like:
SELECT conversation_ID, GROUP_CONCAT(DISTINCT userID ORDER BY userID) as users
FROM conversations_users
GROUP BY conversation_ID
HAVING users = '1,3'
The GROUP BY clause groups the rows having the same conversation_ID and from each group it generates a new record that contains the conversation_ID and the distinct values of userID, in ascending order, concatenated with comma (,).
The HAVING clause keeps only those records that have '1,3' in the column users computed by the GROUP BY clause.
The query produces the output you need but it is not efficient because it reads the entire table. It could be more efficient by picking first the conversations of users 1 and 3 and then applying the above only to them.
It looks like this:
SELECT conversation_ID, GROUP_CONCAT(DISTINCT userID ORDER BY userID) as users
FROM (
SELECT *
FROM conversations_users
WHERE userID in (1, 3)
) conversations
GROUP BY conversation_ID
HAVING users = '1,3'
In order to work faster than the previous query, the conversations_users must have an index on the userID column.
If you want to restrict to those conversations which involve exactly n number of users. I think below generic query should work. Replacing 'n' as per requirement.
select *
from conversations_users
where conversation_id IN (select conversation_id
from conversations_users
group by conversation_id
having count(userid) = 2)
Thanks,
Amitabh
The inner select grabs all conversationIDs with other users than 1 or 3
the outer select (with distinct) collects all conversations wich are NOT in this subset
SELECT DISTINCT conversationID
FROM conversations_users t1
WHERE conversationID NOT IN ( SELECT conversationID
FROM conversations_users
WHERE userID NOT in (1, 3)
)
You can use join with where condition in this case.
SELECT #, userid ,conversation_ID FROM user AS table1 JOIN
conversations_users AS table2
ON user_ID = conversation_ID
WHERE table1.userID = 3 AND
table2.userID = 1
Group by conversation_ID
You can apply suitable condition by where clause instead of group by
I would like to display all orders which have more than 1 item but to display only 2 rows if they have more than 2 items
If you have any ideas i will appreciate it, thank you.
You already have a row number column for each id, which greatly simplifies the query. In this case, we can arrive at your expected output by just joining the ORDERS table to a subquery which identifies id having 2 or more records associated with them.
SELECT t1.*
FROM ORDERS t1
INNER JOIN
(
SELECT id
FROM ORDERS
GROUP BY id
HAVING COUNT(*) >= 2
) t2
ON t1.id = t2.id
WHERE t1.rown_num <= 2
try this simple solution
SELECT * FROM orders od WHERE od.id IN( SELECT id FROM orders o GROUP BY o.id HAVING o.id HAVING COUNT(o.id)>1)
try this:
the first join will give you the rows you want and the second join will add the rown_num field
select a.id,
a.item_num,
b.rown_num
from
(select id,
item_num
from orders
where rown_num in (1,2)
group by id,item_num
having count(id)>=2)a
inner join (select *
from orders )b on a.id=b.id
and a.item_num=b.item_num
How to convert this result:
Group | Sum
Services | 11120.99
Vendas | 3738.00
Into:
Group | Sum
Services | 74.84
Vendas | 25.16
That is, the second displays the results as percentages of total.
This is what I tried:
SELECT categories.cat AS 'Group', SUM(atual) AS 'Sum'
FROM `table1` INNER JOIN
categories
ON table1.category_id=categories.id
GROUP BY categoria
you can left join a total sum that is not grouped or split up, and divide that by your sum query. this way you are just doing the total select once for faster runtime
SELECT cat, sum_atual, sum_atual/total_atual as percent_atual
FROM
( SELECT categories.cat AS cat, SUM(atual) AS sum_atual
FROM `table1`
JOIN categories ON table1.category_id=categories.id
GROUP BY categoria
) t
LEFT JOIN
( SELECT SUM(atual) as total_atual
FROM `table1`
) t1
SELECT categories.cat AS categoria,
SUM(atual) * 100 / (select sum(atual) from table1) AS percentages
FROM `table1`
INNER JOIN categories ON table1.category_id=categories.id
GROUP BY categoria
You can do this several ways. One is to just use a subquery in the select clause. As written below, this assumes that the category_id column in table1 always matches categories:
SELECT c.categoria AS "Group", SUM(t1.atual) AS "Sum",
SUM(t1.atual) / (SELECT SUM(t1.atual) FROM table1) as "Percent"
FROM `table1` t1 INNER JOIN
categories c
ON t1.category_id = c.id
GROUP BY c.categoria;
I changed the group by clause as well. It is a good idea for the group by and select to use the same columns. And I added table aliases to all the column references, another good practice.