SQL Joint 2 table without duplicate rows - mysql

I`m trying to combine 2 tables without duplicate rows
Table 1 - modx_site_content
|id|pagetitle|introtext|pub_date|
---------------------------------
|3635| name1 |texttextt|17.02.2015
|3636| name1 |texttextt|18.02.2015
Table 2 - modx_site_tmplvar_contentvalues
|contentid|tmplvarid|value|
---------------------------
| 3635 | 1 |value1
| 3635 | 1 |value2
| 3636 | 1 |value3
I`m try to make all
|id|title|introtext|publishdate|photo|
--------------------------------------
|3635|name1|texttextt|17.02.2015|value1, value2
|3636|name1|texttextt|18.02.2015|value3
But the current result shows dublicate rows id 3535
|id|title|introtext|publishdate|photo|
--------------------------------------
|3635|name1|texttextt|17.02.2015|value1
|3635|name1|texttextt|17.02.2015|value2
|3636|name1|texttextt|18.02.2015|value3
My current sql resuest is
SELECT
modx_site_content.id,
pagetitle as 'title',
introtext,
pub_date as 'publishdate',
modx_site_tmplvar_contentvalues.value as 'photo'
FROM `modx_site_content`,
`modx_site_tmplvar_contentvalues`
WHERE parent IN (1153,3271)
AND pub_date>0
AND `contentid`= modx_site_content.id
AND `tmplvarid` IN (10, 15, 19)
Order by `pub_date` DESC LIMIT 20

The solution to your immediate problem is group by and group_concat():
SELECT c.id, c.pagetitle as title, c.introtext, c.pub_date as publishdate,
group_concat(cv.value) as sphotos
FROM `modx_site_content` c JOIN
`modx_site_tmplvar_contentvalues` cv
ON cv.`contentid`= c.id
WHERE c.parent IN (1153, 3271) AND c.pub_date > 0 AND
`tmplvarid` IN (10, 15, 19)
GROUP BY c.id, c.pagetitle, c.introtext, c.pub_date
Order by c.`pub_date` DESC
LIMIT 20;
I would also recommend:
Use explicit join syntax.
Define table aliases in the from clause.
Use table aliases for column references.
Don't use single quotes to define column aliases. You don't need an escape character for yours, so don't both using one.

MySQL has group_concat that may work (depending on data type):
SELECT
modx_site_content.id,
pagetitle as 'title',
introtext,
pub_date as 'publishdate',
group_concat(modx_site_tmplvar_contentvalues.value) as 'photo'
FROM `modx_site_content` JOIN
`modx_site_tmplvar_contentvalues` ON `contentid`= modx_site_content.id
WHERE parent IN (1153,3271)
AND pub_date>0
AND `tmplvarid` IN (10, 15, 19)
GROUP BY modx_site_content.id, pagetitle , introtext, pub_date

Related

How to list data from join table in one row?

There 3 entities: 1) Question, 2) Tag and join table between them - question_has_tag.
When I make a select query like:
select * from question_has_tag as qht where qht.question_id = 6;
I'm getting the following result:
question_id| tag_id
6 | 1
6 | 2
6 | 3
and I needed to get:
question_id| tag_id
6 | 1, 2, 3
How to get it ?
You need to GROUP them and use GROUP_CONCAT
Like
SELECT question_id,GROUP_CONCAT(tag_id ORDER BY tag_id )
FROM question_has_tag as qht
WHERE qht.question_id = 6
GROUP BY question_id;
You're looking for the group_concat() function. This function aggregates a particular field, separating them with a arbitrary character(s).
select
question_id
, group_concat(
distinct tag_id
order by tag_id
separator ','
) as tag_id
from
question_has_tags
where
question_id = 6;

Select rows with the same foreign key but another column has a set of values

I have designed a database that stores data in a format like this where both columns are foreign keys leading to different tables. This is a simplified version.
RNA_id | Experiment_id |
1 | a |
1 | b |
2 | a |
2 | b |
2 | c |
3 | b |
4 | a |
4 | c |
I want to select rows that have all three experiment IDs. In this example the results should be
RNA_id | Experiment_id |
2 | a |
2 | b |
2 | c |
I tried
GROUP BY RNA_id HAVING COUNT (DISTINCT Experiment_id)=3
but that just resulted in me having a seemingly random assortment of rows.
The database is already quite large and my query as is is fairly complex already, additionally, I may possibly want to expand to 4 or more Experiment_ids.
Here is a sanitized version of my query as it is complicated and I don't want to explain my whole data structure
My existing query is:
SELECT RNA_id, Experiment_id, <data values>
FROM data
LEFT JOIN ref1
LEFT JOIN ref2
LEFT JOIN ref3
LEFT JOIN ref4
WHERE <required data parameters>
ORDER BY RNA_id
LIMIT 0,5000;
Returns about 700 values, as it should,
but when I change ORDER BY to that GROUP BY command earlier, it returns 9 values all of which have unique RNA_ids and there should be about 100 that get returned.
My RNA_id is actually 3 columns so I may just have to redo the entire database to get any of these solutions to work.
My Solution
I figured out how to make it work with the 3 column identifier
AND (RNA_id1, RNA_id2, RNA_id3) IN (SELECT RNA_id1, RNA_id2, RNA_id3
FROM data
WHERE <parameter>
GROUP BY RNA_id1, RNA_id2, RNA_id3
HAVING COUNT (DISTINCT Experiment_id)=3)
This works with different numbers of Experiment_ids too
I would recommend just returning RNA_id that have all three:
select RNA_id
from t
where Experiment_id in ('a', 'b', 'c')
group by RNA_id
having count(*) = 3;
If you can have duplicates, then use count(distinct experiment_id).
If you want the original rows, then in MySQL 8+, you can adapt this using window functions:
select t.*
from (select t.*, count(*) over (partition by RNA_id) as cnt
from t
where Experiment_id in ('a', 'b', 'c')
) t
where cnt = 3;
SQL DEMO
SELECT *
FROM Table1 t1
WHERE ( SELECT COUNT(DISTINCT `Experiment_id`)
FROM Table1 t2
WHERE t2.`RNA_id` = t1.`RNA_id`
AND t2.Experiment_id in ('a', 'b', 'c') -- if you have more than 3 experiment
) = 3
If a, b and c are the only possible values for Experiment_id, then you need to put your query in the WHERE clause like this:
select *
from tablename
where RNA_id in (
select RNA_id from tablename
group by RNA_id
having count(distinct Experiment_id) = 3
)
If there are other values also:
select *
from tablename
where
Experiment_id in ('a', 'b', 'c')
and RNA_id in (
select RNA_id from tablename
where Experiment_id in ('a', 'b', 'c')
group by RNA_id
having count(distinct Experiment_id) = 3
)
These queries can be easily changed if you want to expand to more than 3 values.

MySQL Merge multiple colums into one 'temporary' column

I was curious about the fact, if it's possible to merge multiple columns (seperated by a comma, or something else) into one column.
So for instance, i have these tables (languages):
language_id | language_iso
1 NL
2 EN
3 GT
4 EN
(Domains)
domain_id | domain_name
1 example
And another table which links the language to a webdomain
languagetodomain_id | languagetodomain_lang | languagetodomain_domain
1 1 1
2 2 1
3 4 1
4 3 1
And retrieve them in this order (ONE column as a reminder)
domain_id | domain_name | TemporaryColumn
1 example {1,2,4,3}
This should work:
SELECT Domains.*, CONCAT('{',GROUP_CONCAT(languagetodomain_lang),'}') AS TemporaryColumn
FROM Domains
JOIN LanguageToDomain ON LanguageToDomain.languagetodomain_domain = Domains.domain_id
GROUP BY domain_id
The function GROUP_CONCAT groups values separating them by a comma. I added the brackets using normal CONCAT function.
You need to group on the domain_id so it takes all the languagetodomain_lang for each domain that you have.
I've guessed the table name for languagetodomain as you didn't provide it.
Spelled out:
Group_Concat allows you to combine data across rows.
SELECT D.Domain_Id
, D.Domain_Name
, Group_Concat(LanguageToDomain_Lang,
ORDER BY LanguageToDomain_ID DESC SEPARATOR ',') as TemporaryColumn
FROM Domains D
INNER JOIN WebDomain WD
on D.Domain_ID = WD.LanguageToDomain_ID
GROUP BY D.Domain_Id, D.Domain_Name

MySQL query by string and limit

I have a table "articles" that has a column "company" which has list of companies or an article type - crappy I know, but it's not my DB. :) Let's say there are n articles for each type. I need to select the first article (based on year, or any other criteria) that is of the type. Something like this:
select * from details where (company = 'aaa' or company = 'bbb' or ...)
I know what the types are, so they can be hardcoded. I need to limit only the first article for each type. Thanks!
EDIT
given the sample data:
id company copy issue
------------------------
1 apple 'abc' NULL
2 bmw 'abc' NULL
3 ibm 'abc' NULL
4 news 'abc' 2
5 news 'abc' 3
6 seagate 'abc' NULL
7 events 'abc' 1
8 features 'abc' 5
9 samsung 'abc' NULL
I need rows 4, 7, 8.
EDIT2
Sorry if I wasn't clear. Essentially the table contains two different types of data. One is company info, and one is article info. Basically I need to do this:
select * from articles where company = "news" order by issue limit 1;
select * from articles where company = "events" order by issue limit 1;
select * from articles where company = "features" order by issue limit 1;
but with a single query.
Something like this perhaps
select * from details d
where company in ('news', 'events', 'features')
and not exists (
select 1 from details d_
where d_.company = d.company
and d_.id < d.id -- provide your sortable criteria here
)
Example here - http://sqlfiddle.com/#!2/bb8f2/6
This query:
select t1.* from t t1
left join t t2
on t1.company = t2.company and t1.id > t2.id
where t2.id is null and t1.company in ('news', 'events', 'features')
will return:
+----+----------+------+
| ID | COMPANY | COPY |
+----+----------+------+
| 4 | news | abc |
| 7 | events | abc |
| 8 | features | abc |
+----+----------+------+
Is that what you're looking for?
Note: When you say the first article I assume the order is provided by the ID field
You can play with it here
Edit:
After your edit, the query is almost the same, just change the ordering field to issue instead of id:
select t1.* from t t1
left join t t2
on t1.company = t2.company and t1.issue > t2.issue
where t2.id is null and t1.company in ('news', 'events', 'features')
Assuming "company" and "publication_date" are columns in table "details", then something like:
select *
from details
where company in ('aaa', 'bbb', ...)
and publication_date between '2012-02-01' and '2012-03-01'
order by publication_date desc
limit 1
You are trying to do a group by.
Next you want to order by the issue column.
Then you only need the first row.
I used group concat to create a list,
then extract the first string in the ordered list group_concat(copy order by copy).
select
id,
company,
substring(
group_concat(copy order by copy),
1,
case when substring_index(group_concat(copy order by copy),',',1) > 1
then substring_index(group_concat(copy order by copy),',',1)-1
else 1 end
) as copy
from details d
where company in ('news', 'events', 'features')
group by company
*the list group_concat(copy order by copy) cannot be too long though, which depends on your data.

Sorting MYSQL Tag table

just wondering if it is possible to get the top 10 COUNT results and ordering by COUNT and alphabetically?
I have the following tables,
tags
-------
id | title
.
tagged
------
tag_id | post_id
And the following SQL query
SELECT tag.*, COUNT(td.tag_ID) AS tagcount
FROM Tagged td
LEFT JOIN Tags tag ON td.tag_ID = tag.tag_ID
GROUP BY td.tag_ID
ORDER BY tagcount DESC, tag.tag_Title ASC
Any ideas?
Thanks in advance
Edit
Sorry if I didnt explain it properly.
The query works and I didnt add LIMIT 10 due to wanting to see the entire result set first.
The query I have works, however at the following example result
tag_ID tag_Title tagcount
1 Science 3
3 Chemistry 2
4 Misc 1
5 Maths 1
2 Sport 1
I would want Chemistry to come above Science though.
i.e. top ten highest counts.. sorted alphabetically
Result
Thanks to you both.. Daniel and Sled.
Here is a working example
(
SELECT t.*, COUNT(*) AS tagcount
FROM tagged td
LEFT JOIN tags t ON (t.id = td.tag_id)
GROUP BY td.tag_id
ORDER BY tagcount DESC, t.title ASC
LIMIT 3
) ORDER BY title ASC;
UPDATE:
Further to the new comment below:
(
SELECT t.*, COUNT(*) AS tagcount
FROM tagged td
LEFT JOIN tags t ON (t.id = td.tag_id)
GROUP BY td.tag_id
ORDER BY tagcount DESC, t.title ASC
LIMIT 3
) ORDER BY title ASC;
Result:
+------+------------+----------+
| id | title | tagcount |
+------+------------+----------+
| 3 | javascript | 2 |
| 1 | mysql | 2 |
| 2 | php | 3 |
+------+------------+----------+
3 rows in set (0.00 sec)
Simply change the LIMIT 3 to LIMIT 10 to get the top 10 instead of the top 3.
Previous Answer:
Why don't you add a LIMIT 10 to your query?
SELECT t.*, COUNT(*) AS tagcount
FROM tagged td
LEFT JOIN tags t ON (t.id = td.tag_id)
GROUP BY td.tag_id
ORDER BY tagcount DESC, t.title ASC
LIMIT 10;
Test case:
CREATE TABLE tags (id int, title varchar(20));
CREATE TABLE tagged (tag_id int, post_id int);
INSERT INTO tags VALUES (1, 'mysql');
INSERT INTO tags VALUES (2, 'php');
INSERT INTO tags VALUES (3, 'javascript');
INSERT INTO tags VALUES (4, 'c');
INSERT INTO tagged VALUES (1, 1);
INSERT INTO tagged VALUES (2, 1);
INSERT INTO tagged VALUES (1, 2);
INSERT INTO tagged VALUES (2, 2);
INSERT INTO tagged VALUES (3, 3);
INSERT INTO tagged VALUES (2, 4);
INSERT INTO tagged VALUES (3, 4);
INSERT INTO tagged VALUES (4, 5);
Result (using LIMIT 3):
+------+------------+----------+
| id | title | tagcount |
+------+------------+----------+
| 2 | php | 3 |
| 3 | javascript | 2 |
| 1 | mysql | 2 |
+------+------------+----------+
3 rows in set (0.00 sec)
Note how the [c] tag fell out of the top 3 results, and rows are ordered alphabetically in case of a tie.
Does the query work? If yes, you could use LIMIT 0, 10 to get only the top 10 rows.
SELECT tag.*, COUNT(td.tag_ID) AS tagcount
FROM Tagged td
LEFT JOIN gen_Tags tag ON td.tag_ID = tag.tag_ID
GROUP BY td.tag_ID
ORDER BY tagcount DESC, tag.tag_Title ASC LIMIT 0, 10
Another thing you might be interested in, is ranking. See here: http://www.fromdual.com/ranking-mysql-results
Edit
Maybe a subquery does what you want:
SELECT list.* FROM (
SELECT tag.*, COUNT(td.tag_ID) AS tagcount,
FROM Tagged td
LEFT JOIN gen_Tags tag ON td.tag_ID = tag.tag_ID
GROUP BY td.tag_ID
ORDER BY tagcount DESC LIMIT 0, 10
) AS list ORDER BY list.tag_Title ASC, list.tagcount DESC