I have a table containing the last comments posted on the website, and I'd like to join a different table depending on the comment type.
Comments Table is similar to this structure:
id | type | ressource_id |
---+------+--------------+
1 | 1 | 10 |
2 | 3 | 7 |
3 | 3 | 12 |
4 | 1 | 22 |
5 | 4 | 22 |
6 | 5 | 23 |
News Table:
news_id | notes| date |
--------+------+--------------+
10 | | 2015-08-12 |
22 | | 2015-07-12 |
Tutorial Table:
tuto_id | notes| date |
--------+------+--------------+
7 | | 2015-06-15 |
12 | | 2015-05-14 |
... Similar table for type = 4, 5, 6
Now in order to get specific comments I am doing a left join on the two tables.
SELECT co.*
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3
WHERE (co.type IN (1,3))
I am interested in getting the date from the left table. How can I include that column in output list.
Result desired: (date from joining table)
id | type | ressource_id | date |
---+------+--------------+--------------+
1 | 1 | 10 | 2015-08-12 |
2 | 3 | 7 | 2015-06-15 |
3 | 3 | 12 | 2015-05-14 |
4 | 1 | 22 | 2015-07-12 |
Thanks.
Since you will never get a date from News and Tutorial for the same comment you might go withCOALESCE`:
SELECT co.*, COALESCE(n.date,t.date)
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3
WHERE (co.type IN (1,3))
COALESCE will return the first argument that is not null, so if there is a matching news it will return the date from News and if there is no matching news but a matching tutorial it will return the date from Tutorial.
try this:
SELECT co.*,coalesce(n.date,t.date)
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3
WHERE (co.type IN (1,3))
and (co.id = n.news_id or co.id = t.tuto_id)
You can use a UNION operator also.
(SELECT co.*,n.date as [date]
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1)
UNION ALL
(SELECT co.*,t.date
FROM Comments co
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3)
ORDER BY ID
Related
Blog table:
| bid | btitle |
| 29 | ...... |
| 38 | ...... |
likes table:
| lid | bid |
| 1 | 29 |
| 2 | 29 |
| 3 | 29 |
| 4 | 38 |
| 5 | 38 |
comment table
| commid | bid |
| 1 | 29 |
| 2 | 29 |
| 3 | 38 |
I had tried the following query but that will not work for me:
SELECT blog.bid,blog.btitle,COUNT(likes.lid) AS likecnt,COUNT(comment.comid) AS commentcnt FROM blog,likes,comment WHERE blog.bid=likes.bid AND blog.bid=comment.bid GROUP BY blog.bid
i want output like:
| bid | btitle | likecnt | commentcnt |
| 29 | ...... | 3 | 2 |
| 38 | ...... | 2 | 1 |
You can do left join with separate aggregation :
select b.bid, b.btitle,
coalesce(l.likecnt, 0) as likecnt,
coalesce(c.commentcnt, 0) as commentcnt
from blog b left join
(select l.bid, count(*) as likecnt
from likes l
group by l.bid
) l
on l.bid = b.bid left join
(select c.bid, count(*) as commentcnt
from comment c
group by c.bid
) c
on c.bid = l.bid;
If you want only matching bids the use INNER JOIN instead of LEFT JOIN & remove COALESCE().
Under many circumstances, correlated subqueries may be the fastest solution:
select b.bid, b.btitle,
(select count(*) from likes l where l.bid = b.bid) as num_likes,
(select count(*) from comment c where c.bid = b.bid) as num_comments
from blog b;
When is this a win performance wise. First, you want indexes on likes(bid) and comments(bid). With those indexes, it might be the fastest approach for your query.
It is particularly better if you have a where clause filtering the blogs in the outer query. It only has to do the counts for the blogs in the result set.
Use proper joins and count DISTINCT values because multiple joins increase the number of returned rows:
SELECT b.bid, b.btitle,
COUNT(DISTINCT l.lid) AS likecnt,
COUNT(DISTINCT c.comid) AS commentcnt
FROM blog b
LEFT JOIN likes l ON b.bid = l.bid
LEFT JOIN comment c ON b.bid = c.bid
GROUP BY b.bid, b.btitle
See the demo.
I use LEFT joins just in case there are no comments or likes for a post.
Results:
| bid | btitle | likecnt | commentcnt |
| --- | ------ | ------- | ---------- |
| 29 | ...... | 3 | 2 |
| 38 | ...... | 2 | 1 |
I have one query about fetch the data.
table name:sub_element
se_id | se_name
1 | GHSB
2 | ENGLISH
3 | GUJ
4 | RUSSIAN
5 | FRENCH
6 | S1
7 | S2
8 | S3
table name = class_standard
cs_id | board_se_id | medium_se_id | s_se_id
1 | 1 | 2 | 6,7
2 | 3 | 4 | 6,8
table name:class_standard_subject
css_id | cs_id | se_id
1 | 1 | 6
2 | 1 | 7
3 | 2 | 6
4 | 2 | 8
expected output is:
GHSB | ENGLISH | S1,S2
GUJ | RUSSIAN | S1,S3
Both data are from same table.
How to achieve this type of output.
Please help.
I tried this query but not getting expected output:
select t1.*,a1.* FROM ((
SELECT cs.cs_id, se.se_name as bname FROM sub_element se, class_standard cs WHERE cs.board = se.se_id GROUP BY cs.cs_id) as a1,
(SELECT se.se_name as mname FROM sub_element se, class_standard cs WHERE cs.medium = se.se_id GROUP BY cs.cs_id ) as t1)
You need to join sub_element 2 times as
select
se1.se_name as se_name1,
se2.se_name as se_name2
from class_standard cs
join sub_element se1 on se1.id = cs.board_se_id
join sub_element se2 on se2.id = cs.medium_se_id
UPDATE : With the updated question you have comma separated values and you should avoid these since its not properly normalized and lead into many issues in future.
However you can achieve the result as
select
cs.cs_id,se1.se_name as se_name1,
se2.se_name as se_name2,
group_concat(se3.se_name) as se_name3
from class_standard cs
join sub_element se1 on se1.se_id = cs.board_se_id
join sub_element se2 on se2.se_id = cs.medium_se_id
left join sub_element se3 on find_in_set(se3.se_id,cs.s_se_id)
group by cs.cs_id ;
In this example, I have a listing of users (main_data), a pass list (pass_list) and a corresponding priority to each pass code type (pass_code). The query I am constructing is looking for a list of users and the corresponding pass code type with the lowest priority. The query below works but it just seems like there may be a faster way to construct it I am missing. SQL Fiddle: http://sqlfiddle.com/#!2/2ec8d/2/0 or see below for table details.
SELECT md.first_name, md.last_name, pl.*
FROM main_data md
JOIN pass_list pl on pl.main_data_id = md.id
AND
pl.id =
(
SELECT pl2.id
FROM pass_list pl2
JOIN pass_code pc2 on pl2.pass_code_type = pc2.type
WHERE pl2.main_data_id = md.id
ORDER BY pc2.priority
LIMIT 1
)
Results:
+------------+-----------+----+--------------+----------------+
| first_name | last_name | id | main_data_id | pass_code_type |
+------------+-----------+----+--------------+----------------+
| Bob | Smith | 1 | 1 | S |
| Mary | Vance | 8 | 2 | M |
| Margret | Cough | 5 | 3 | H |
| Mark | Johnson | 9 | 4 | H |
| Tim | Allen | 13 | 5 | M |
+------------+-----------+----+--------------+----------------+
users (main_data)
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | Bob | Smith |
| 2 | Mary | Vance |
| 3 | Margret | Cough |
| 4 | Mark | Johnson |
| 5 | Tim | Allen |
+----+------------+-----------+
pass list (pass_list)
+----+--------------+----------------+
| id | main_data_id | pass_code_type |
+----+--------------+----------------+
| 1 | 1 | S |
| 3 | 2 | E |
| 4 | 2 | H |
| 5 | 3 | H |
| 7 | 4 | E |
| 8 | 2 | M |
| 9 | 4 | H |
| 10 | 4 | H |
| 11 | 5 | S |
| 12 | 3 | S |
| 13 | 5 | M |
| 14 | 1 | E |
+----+--------------+----------------+
Table which specifies priority (pass_code)
+----+------+----------+
| id | type | priority |
+----+------+----------+
| 1 | M | 1 |
| 2 | H | 2 |
| 3 | S | 3 |
| 4 | E | 4 |
+----+------+----------+
Due to mysql's unique extension to its GROUP BY, it's simple:
SELECT * FROM
(SELECT md.first_name, md.last_name, pl.*
FROM main_data md
JOIN pass_list pl on pl.main_data_id = md.id
ORDER BY pc2.priority) x
GROUP BY md.id
This returns only the first row encountered for each unique value of md.id, so by using an inner query to order the rows before applying the group by you get only the rows you want.
A version that will get the details as required, and should also work across different flavours of SQL
SELECT md.first_name, md.last_name, MinId, pl.main_data_id, pl.pass_code_type
FROM main_data md
INNER JOIN pass_list pl
ON md.id = pl.main_data_id
INNER JOIN pass_code pc
ON pl.pass_code_type = pc.type
INNER JOIN
(
SELECT pl.main_data_id, pl.pass_code_type, Sub0.MinPriority, MIN(pl.id) AS MinId
FROM pass_list pl
INNER JOIN pass_code pc
ON pl.pass_code_type = pc.type
INNER JOIN
(
SELECT main_data_id, MIN(priority) AS MinPriority
FROM pass_list a
INNER JOIN pass_code b
ON a.pass_code_type = b.type
GROUP BY main_data_id
) Sub0
ON pl.main_data_id = Sub0.main_data_id
AND pc.priority = Sub0.MinPriority
GROUP BY pl.main_data_id, pl.pass_code_type, Sub0.MinPriority
) Sub1
ON pl.main_data_id = Sub1.main_data_id
AND pl.id = Sub1.MinId
AND pc.priority = Sub1.MinPriority
ORDER BY pl.main_data_id
This does not rely on the flexibility of MySQLs GROUP BY functionality.
I'm not familiar with the special behavior of MySQL's group by, but my solution for these types of problems is to simply express as where there doesn't exist a row with a lower priority. This is standard SQL so should work on any DB.
select distinct u.id, u.first_name, u.last_name, pl.pass_code_type, pc.id, pc.priority
from main_data u
inner join pass_list pl on pl.main_data_id = u.id
inner join pass_code pc on pc.type = pl.pass_code_type
where not exists (select 1
from pass_list pl2
inner join pass_code pc2 on pc2.type = pl2.pass_code_type
where pl2.main_data_id = u.id and pc2.priority < pc.priority);
How well this performs is going to depend on having the proper indexes (assuming that main_data and pass_list are somewhat large). In this case indexes on the primary (should be automatically created) and foreign keys should be sufficient. There may be other queries that are faster, I would start by comparing this to your query.
Also, I had to add distinct because you have duplicate rows in pass_list (id 9 & 10), but if you ensure that duplicates can't exist (unique index on main_data_id, pass_code_type) then you will save some time by removing the distinct which forces a final sort of the result set. This savings would be more noticeable the larger the result set is.
So I've got 2 tables (simplified below)
members documents
------------ ------------------
id | name | registered id | member_id | type | expiry
---------------------- ------------------------------
1 | AAA | 1234567890 1 | 1 | 1 | 1234567890
2 | BBB | 1234567890 2 | 1 | 2 | 1234567891
3 | CCC | 1234567890 3 | 1 | 3 | 1234567892
4 | 2 | 1 | 1234567893
5 | 2 | 2 | 1234567894
6 | 2 | 3 | 1234567890
and I need to display these like this:
member id | name | doc 1 expiry | doc 2 expiry | doc 3 expiry
--------------------------------------------------------------
1 | AAA | 1234567890 | 1234567891 | 1234567892
2 | BBB | 1234567893 | 1234567894 | 1234567895
I've tried querying with multiple outer joins and aliases but it's just repeating the document expiry timestamps. This is what I have so far:
SELECT DISTINCT `members`.`id`, `members`.`name`, `a`.`expiry` AS `expiry1`, `b`.`expiry` AS `expiry2`, `c`.`expiry` AS `expiry3`
FROM `members`
LEFT OUTER JOIN `documents` a ON `a`.`member_id` = `members`.`id`
LEFT OUTER JOIN `documents` b ON `b`.`member_id` = `members`.`id`
LEFT OUTER JOIN `documents` c ON `c`.`member_id` = `members`.`id`
GROUP BY `members`.`id`
People need to be able to search through this, for example to list everyone whose document type 3 has expired.
Try
SELECT
a.id AS 'member id',
a.name
SUM(a.d1exp) AS 'doc 1 expiry',
SUM(a.d2exp) AS 'doc 2 expiry',
SUM(a.d3exp) AS 'doc 3 expiry'
FROM
(
SELECT
aa.id,
aa.name,
COALESCE(d1.expiry, 0) AS d1exp,
COALESCE(d2.expiry, 0) AS d2exp,
COALESCE(d3.expiry, 0) AS d3exp
FROM
members aa
LEFT JOIN
documents d1 ON aa.id = d1.member_id AND d1.type = 1
LEFT JOIN
documents d2 ON aa.id = d2.member_id AND d2.type = 2
LEFT JOIN
documents d3 ON aa.id = d3.member_id AND d3.type = 3
) a
GROUP BY
a.id,
a.name
This is assuming the values in the 'expiry' field are numerical.
Users
+--------+--------------+----------+--------------+
| userID | userUsername | userName | userLastName |
+--------+--------------+----------+--------------+
| 6 | richard | Ricardo | Vega |
| 10 | jason | Jason | Bourne |
+--------+--------------+----------+--------------+
Restocks
+-----------+-------------+--------+--------+-----------------+
| restockID | restockDate | itemID | userID | restockQuantity |
+-----------+-------------+--------+--------+-----------------+
| 1 | 2012-02-29 | 1 | 6 | 48 |
| 2 | 2012-02-29 | 1 | 10 | 100 |
| 3 | 2012-02-29 | 2 | 10 | 50 |
| 4 | 2012-02-29 | 2 | 6 | 100 |
| 5 | 2012-02-29 | 2 | 6 | 200 |
| 6 | 2012-02-29 | 2 | 10 | 2000 |
| 7 | 2012-02-29 | 1 | 10 | 2000 |
+-----------+-------------+--------+--------+-----------------+
Items
+--------+--------------------+
| itemID | itemName |
+--------+--------------------+
| 1 | Coca Cola (lata) |
| 2 | Cerveza Sol (lata) |
+--------+--------------------+
Ok guys, i have supplied some sample data as requested. I need to get this table:
+--------+--------------------+---------------+-------------+----------+--------------+--------------+
| itemID | itemName | itemExistence | restockDate | userName | userLastName | userUsername |
+--------+--------------------+---------------+-------------+----------+--------------+--------------+
| 2 | Cerveza Sol (lata) | 2350 | 2012-02-29 | Jason | Bourne | jason |
| 1 | Coca Cola (lata) | 2148 | 2012-02-29 | Ricardo | Vega | richard |
+--------+--------------------+---------------+-------------+----------+--------------+--------------+
But, i need restockDate to be THE LATEST ONE for each itemName. In the example, it shows the first restock and not the latest one. I just need to show what's the existence for the item and when was restocked for last time, not first time.
If my tables are not good or so, please suggest a new schema.
I know maybe this is a lot so i will tip 5 USD (Paypal) to the one how can help me with this. Promise.
As discussed in comments, many restocks can be performed on the same day so it is not possible to compare dates in this case. Two options are presented here: Use the incremental PK from restocks table or restructure the table. For the first case, this is the solution:
select i.itemID, i.itemName, i.itemExistence, r.restockDate, u.userName,
u.userLastName, u.userUsername
from items i
left join (
select r1.restockDate, r1.itemID, r1.userID from restocks r1
left join restocks r2
on r1.itemId = r2.itemId and r1.restockId < r2.restockId
where r2.restockDate is null
) as r on i.itemID = r.itemID
inner join users u on r.userID = u.userID
For the second case, the restructre would imply changing the date field to a unique datetime that would uniquely identify a record. That is the best solution, however, it does require to also update any previous data present in the table. That means, to update all the records that have the same date for a single product restock and set different date times to them.
The lazy one (like me), would go for the first option :) Let me know if you have any doubt about this.
first get the distinct from items table and then use it to join others
SELECT items.*, restocks.restockDate, users.userName, users.userLastName, users.userUsername
FROM (SELECT DISTINCT items.itemID, items.itemName, items.itemExistence FROM items) AS items
LEFT JOIN restocks on items.itemID = restocks.itemID
LEFT JOIN users on restocks.userID = users.userID
GROUP BY items.itemName
Not Tested
UPDATED
select items.itemID, items.itemName, items.itemExistence, restocks.restockDate, users.userName, users.userLastName, users.userUsername
from items
inner join restocks on items.itemID = restocks.itemID
inner join users on restocks.userID = users.userID
GROUP BY items.itemName
select
items.itemID, items.itemName, items.itemExistence,
(select A.restockDate from restocks A where A.itemId = items.itemID limit 0, 1),
(select B.userID from restocks B where B.itemId = items.itemID limit 0, 1),
users.userName, users.userLastName, users.userUsername
from items
left join users on B.userID = users.userID
Please try this.
You don't mention what itemExistence is, so I'm hoping it's a column in the Items table.
Here's an easy way to do it with a self-join:
SELECT i.itemID, i.itemName, i.itemExistence, r1.restockDate,
u.userName, u.userLastName, u.userUsername
FROM Items i
JOIN Restocks r1
ON r1.itemID = i.itemID
JOIN Users u
ON u.userID = r1.userID
LEFT JOIN Restocks r2
ON r2.itemID = i.itemID
AND r2.restockDate > r1.restockDate
WHERE r2.itemID IS NULL
The LEFT JOIN with the WHERE clause ensures that we only pull the row with the latest restockDate.
The advantage of this approach is that it avoids subqueries, which often negate the use of indexes.
You can get duplicate records for a particular item if it was restocked more than once on the same date.