SELECT DISTINCT 2 columns in a INNER JOIN - mysql

I'm having a problem with a query for MySQL, i have to get rows with distinct "c.email" AND distinct "pf.cliente_id", I'm doing this code below, but i'm not getting what i want, the "c.email" don't returns DISTINCT
SELECT DISTINCT c.nome as nome, pf.id_cliente as cliente_id
FROM cliente c
INNER JOIN pessoa_fisica pf
ON c.id_cliente = pf.id_cliente
GROUP BY c.email, pf.id_cliente;
I Also tried:
SELECT c.nome as nome, pf.id_cliente as cliente_id
FROM cliente c
INNER JOIN pessoa_fisica pf
ON c.id_cliente = pf.id_cliente
GROUP BY c.email, pf.id_cliente;
Edit
cliente = email( It's not the primary key, is a foreign key and i have it reapeted )
pessoa_fisica = id_cliente ( It's not the primary key, is a foreign key and i have it reapeted )

this query world work for you may be syntax error there because i have not database.
select SUBSTRING_INDEX(full, ' ', 1) AS email ,SUBSTRING_INDEX(full, ' ', -1) AS cliente_id , nome
from
(SELECT DISTINCT (concat(c.email as email,' ',pf.id_cliente )) as 'full' , c.nome as 'nome'
FROM cliente c
INNER JOIN pessoa_fisica pf
ON c.id_cliente = pf.id_cliente) as temp;
Mysql distinct doesn't allow multiple values, but apply little effort means first concat columns and then apply distinct then break column again. I am setting an example for you.
Create Table: CREATE TABLE `dupli` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fname` varchar(20) DEFAULT NULL,
`lname` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
insert into dupli (fname,lname) values ('Hitesh', 'mundra'),('Neeraj', 'sharma'),('Kailash','yadav');
insert into dupli (fname,lname) values ('Hitesh', 'mundra'),('Neeraj', 'sharma'),('Kailash','yadav');
select id, SUBSTRING_INDEX(name, ' ', 1) AS fname ,SUBSTRING_INDEX(name, ' ', -1) AS lname from (select distinct( concat(fname," ",lname) ) as Name, id from dupli) as temp;

Related

Mysql find the book with the highest rating for each country. If there is a tie "print" the book with the highest number of ratings

I have the following tables:
CREATE TABLE `country` (
`name` VARCHAR(60) NOT NULL,
`code` VARCHAR(3) UNIQUE NOT NULL,
PRIMARY KEY (`code`)
);
CREATE TABLE `user` (
`userId` INT UNIQUE NOT NULL AUTO_INCREMENT,
`country` VARCHAR(3) NOT NULL,
`age` INT NOT NULL,
PRIMARY KEY (`userId`),
CONSTRAINT `fk_user_country` FOREIGN KEY (`country`) REFERENCES `country`(`code`)
);
CREATE TABLE `bookRating` (
`userId` INT NOT NULL,
`isbn` VARCHAR(13) NOT NULL,
`rate` INT NOT NULL,
`date` DATE NOT NULL,
CONSTRAINT `fk_bookRating_user` FOREIGN KEY (`userId`) REFERENCES `user`(`userId`),
CONSTRAINT `fk_bookRating_book` FOREIGN KEY (`isbn`) REFERENCES `book`(`isbn`)
);
CREATE TABLE `book` (
`isbn` varchar(13) UNIQUE NOT NULL,
`bookTitle` VARCHAR(280),
`bookAuthor` VARCHAR(150),
`yearPublication` int(4),
-- `yearPublication` must be an integer because we have value less that 1901 in dataset
`publisher` VARCHAR(135),
PRIMARY KEY (`isbn`),
CONSTRAINT `publication_yea_chk` check ((`yearPublication` > -1) && (`yearPublication` < 2101))
);
As I am saying on the title I want to find the book with the highest average rating, For each country
I have tried this query:
select T1.name, T1.BookTitle, Rate
from
(
select C.Code, AVG(BR.rate) MAXRating
from `bookRating` BR
inner join `book` B on BR.isbn = B.isbn
INNER JOIN `USER` U ON BR.UserID = U.USERId
INNER JOIN `COUNTRY` C ON U.country = C.Code
group by C.Code
) T
inner join
(
select C.Code, C.name, B.BookTitle, BR.ISBN, BR.rate
from `bookRating` BR
inner join `book` B on BR.isbn = B.isbn
INNER JOIN `USER` U ON BR.UserID = U.USERId
INNER JOIN `COUNTRY` C ON U.country = C.Code
) T1 ON T.Code = T1.Code AND T.MAXRATING = T1.RATE;
I am pretty sure this works. But I want to make it like If 2 or more books have the same average rating I want the one with the highest number of ratings.
I figured that I could use an If() statement, but how could I If(... , a condition)
How could I do it?
UPDATE
I have made the database and inserted some info in db fidle:
https://www.db-fiddle.com/f/s6wKhKhxXMX1W2x9VZn9da/1
You can join the tables, aggregate by country and book to get all average ratings and use window functions MAX() and FIRST_VALUE() on the results of the aggregation to get the book with the highest average for each country:
SELECT DISTINCT c.name,
FIRST_VALUE(b.bookTitle) OVER (
PARTITION BY c.Code
ORDER BY AVG(r.rate) DESC, COUNT(*) DESC
) bookTitle,
MAX(AVG(r.rate)) OVER () AverageRating
FROM country c
INNER JOIN users u ON u.country = c.Code
INNER JOIN bookRating r ON r.UserID = u.UserID
INNER JOIN book b ON b.isbn = r.isbn
GROUP BY c.Code, b.isbn;
See the demo.

how to use left join to output results from a column that are null using two different data types?

hi im having trouble doing a left join that outputs all customers who haven't made an order yet. im getting a conversion error and have tried using cast to convert it but it doesnt work. any help would be appreciated
here are my tables and data:`
create table Customers(
Cust_code varchar(2) primary key ,
[first_name] varchar(30) not null,
last_name varchar(30) not null,
address varchar(100) not null,
city varchar(35) not null)
create table Cust_Order(
order_no int ,
Cust_code varchar(2) not null,
order_date date not null,
meth_pmt varchar(30) not null,
constraint Ord_order_no_pk primary key(order_no),
constraint Cus_cust_code_fk foreign key(Cust_code) references Customers)
create table Product(
product_id int ,
product_name varchar(100) not null,
product_price decimal(8, 2) not null,
constraint Pro_product_id_pk primary key(product_id))
create table Order_Line(
order_no int ,
product_id int ,
qty int not null,
sale_price decimal(8,2) not null,
constraint Ord_order_no_product_id_pk primary key(order_no, product_id),
constraint Pro_product_id_fk foreign key(product_id) references Product)
insert into Customers (Cust_code, [first_name], last_name, address, city)
values('A1', 'Kath', 'Morgan','122 Lilain Street', 'Palmerston North'),
('A2','Mike','Smith','67 Golf Hill Drive','Wellington'),
('A3','Glen','Hoddle','San Quentin Ave','Palmerston North'),
('A4','Dan','Boone','Alamo Road','Wellington')
insert into Cust_Order (order_no, Cust_code, order_date, meth_payment)
values(1, 'A1','2014-01-16','CC'),
(2, 'A1','2014-02-16','CC'),
(3, 'A2','2014-01-16','CHEQUE'),
(4, 'A3','2014-03-17','CC')
insert into Product (product_id, product_name, product_price)
values(1, 'Network Card', 58.00),
(2, 'Motherboard', 150.00),
(3, 'Video Card', 232.00)
insert into Order_Line (order_no, product_id, qty, sale_price)
values(1,1,3,70.00),
(1,2,1,170.00),
(1,3,2,300.00),
(2,1,2,70.00),
(2,3,1,300.00),
(3,1,2,70.00),
(4,1,3,70.00)
my select statement using left join which should output the one customer who hasn't made an order
select first_name + last_name as 'Customers who have not made an order',
order_no from Customers as c
left join Cust_Order as o
on c.Cust_code = o.order_no
where order_no is null
Your join condition needs to match on the customer codes. Other than this, the logic of your query is correct, but MySQL doesn't use + for string concatenation, this is SQL Server syntax. Use the CONCAT function instead:
SELECT
CONCAT(c.first_name, ' ', c.last_name) AS `Customers who have not made an order`
FROM Customers AS c
LEFT JOIN Cust_Order AS o
ON c.Cust_code = o.Cust_code
WHERE o.Cust_code IS NULL;
The ANSI concatenation operator is ||, and you may also use it in MySQL if you set the appropriate mode:
SET sql_mode = PIPES_AS_CONCAT
If you must use a LEFT JOIN, try:
SELECT
CONCAT(C.first_name, ' ', C.last_name) AS 'Customers who have not made an order'
FROM Customers C LEFT JOIN Cust_Order CO
ON C.Cust_code=CO.Cust_code
GROUP BY Customer_FullName
HAVING COUNT(B.*)=0;
Or you can make use of EXISTS.
SELECT
CONCAT(C.first_name, ' ', C.last_name) AS 'Customers who have not made an order'
FROM Customers C
WHERE NOT EXISTS (SELECT NULL FROM Cust_Order CO WHERE C.Cust_code=CO.Cust_code);
See MySQL join made easy for insights on using joins.

How to make use of index on LEFT OUTER JOIN

Here is a set of tables describing music composers :
CREATE TABLE IF NOT EXISTS `compositors` (
`id` int(11) NOT NULL,
`name` varchar(45) NOT NULL COMMENT 'Nom et Prenom',
`birth_date` varchar(45) DEFAULT NULL,
`death_date` varchar(45) DEFAULT NULL,
`birth_place` varchar(45) DEFAULT NULL,
`death_place` varchar(45) DEFAULT NULL,
`gender` enum('M','F') DEFAULT NULL,
`century` varchar(45) DEFAULT NULL,
`country` int(11) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=28741 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `compositor_biography` (
`index` int(11) NOT NULL,
`compositor_id` int(11) NOT NULL,
`url` varchar(255) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=15325 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `compositor_comments` (
`compositor_id` int(11) NOT NULL,
`comment` text NOT NULL,
`public` enum('Publique','Privé') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `compositor_country` (
`compositor_id` int(11) NOT NULL,
`country_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Here are my indexes :
--
-- Index pour la table `compositors`
--
ALTER TABLE `compositors` ADD PRIMARY KEY (`id`), ADD KEY `countries` (`country`);
ALTER TABLE `compositor_biography` ADD PRIMARY KEY (`index`), ADD KEY `index` (`compositor_id`);
ALTER TABLE `compositor_comments` ADD KEY `c_compositor_idx` (`compositor_id`);
And finally sample data :
INSERT INTO `compositors` (`id`, `name`, `birth_date`, `death_date`, `birth_place`, `death_place`, `gender`, `century`, `country`) VALUES
(1, 'Dummy Compositor', '1606', '1676', 'Bruxellesss', NULL, 'F', '17', 11);
INSERT INTO `compositor_biography` (`index`, `compositor_id`, `url`) VALUES
(15322, 1, 'Dummy Link 1'),
(15323, 1, 'Dummy Link 2'),
(15324, 1, 'Dummy Link 3');
INSERT INTO `compositor_comments` (`compositor_id`, `comment`, `public`) VALUES
(1, 'Dummy Comment', 'Privé');
Here is an example query that my PHP script generate :
SELECT DISTINCT compositors.id, compositors.name, compositors.birth_date, compositors.death_date, compositors.birth_place, compositors.death_place, compositors.gender, compositors.century, compositors.country,
GROUP_CONCAT( compositor_biography.url SEPARATOR ';') AS concat_compositor_biography_url,
GROUP_CONCAT( compositor_comments.comment SEPARATOR ';') AS concat_compositor_comments_comment,
GROUP_CONCAT( compositor_comments.public + 0 SEPARATOR ';') AS concat_compositor_comments_public
FROM compositors
LEFT JOIN compositor_biography ON compositors.id = compositor_biography.compositor_id
LEFT JOIN compositor_comments ON compositors.id = compositor_comments.compositor_id
GROUP BY compositors.id
However, this one has a problem, if you execute this query, you can see that in the concat_compositor_comments_comment column, you have that result :
Dummy Comment;Dummy Comment;Dummy Comment
but there is only one actual comment.
I didn't really understand what was the problem there, but it seemed like it was the GROUP BY. It should have one GROUP BY per JOIN - according to the second answer at Multiple GROUP_CONCAT on different fields using MySQL -- so I did it, and it worked, with this query :
SELECT DISTINCT compositors.id,
compositors.NAME,
compositors.birth_date,
compositors.death_date,
compositors.birth_place,
compositors.death_place,
compositors.gender,
compositors.century,
compositors.country,
concat_compositor_biography_url,
concat_compositor_comments_comment,
concat_compositor_comments_public
FROM compositors
LEFT JOIN (
SELECT compositor_id,
GROUP_CONCAT(compositor_biography.url SEPARATOR ';') AS concat_compositor_biography_url
FROM compositor_biography
GROUP BY compositor_biography.compositor_id
) compositor_biography ON compositors.id = compositor_biography.compositor_id
LEFT JOIN (
SELECT compositor_id,
GROUP_CONCAT(compositor_comments.comment SEPARATOR ';') AS concat_compositor_comments_comment,
GROUP_CONCAT(compositor_comments.PUBLIC + 0 SEPARATOR ';') AS concat_compositor_comments_public
FROM compositor_comments
GROUP BY compositor_comments.compositor_id
) compositor_comments ON compositors.id = compositor_comments.compositor_id
However, this query has huge performance problem, since it doesn't use INDEXES, or at least it seems to scan all the tables, and with 24000 composers, it takes approx 420 second for that query, while the other ( which gives wrong results on GROUP BY ) takes 1 second.
How can I change the second query, so it uses correctly the index and doesn't scan all the tables ?
Here is a link to a SQL-Fiddle the database schema : http://sqlfiddle.com/#!2/6b0132
UPDATE
According to #phil_w, and after further testing, this query seems to work with very good performance :
SELECT a.id,
a.name,
a.concat_compositor_biography_url,
b.concat_compositor_aliases_data,
GROUP_CONCAT(compositor_comments.comment SEPARATOR ';') as concat_compositor_comments_comment,
GROUP_CONCAT(compositor_comments.public + 0 SEPARATOR ';') as concat_compositor_comments_public
FROM (
SELECT b.id,
b.name,
b.concat_compositor_biography_url,
GROUP_CONCAT(compositor_aliases.data SEPARATOR ';') as concat_compositor_aliases_data
FROM (
SELECT compositors.id,
compositors.name,
GROUP_CONCAT(compositor_biography.url SEPARATOR ';') AS concat_compositor_biography_url
FROM compositors
LEFT JOIN compositor_biography ON compositors.id = compositor_biography.compositor_id
GROUP BY compositors.id
) b
LEFT JOIN compositor_aliases ON b.id = compositor_aliases.compositor_id
GROUP BY b.id
) a
LEFT JOIN compositor_comments ON a.id = compositor_comments.compositor_id
GROUP BY a.id
However, how would it be possible to have the same result in a more compact query ? ( by the way, shall I create a new question for that and make this one resolved ? )
This question has nothing to do with "indexes". The problem is that you have two joins and every combination of rows will be returned (ie you have 3 matching rows in the other join to compositor_biography).
The fix is simple - just add DISTINCT to the GROUP_CONCAT() function:
...
GROUP_CONCAT( DISTINCT compositor_comments.comment SEPARATOR ';') AS concat_compositor_comments_comment,
...
It's normal that you have the entry 3 times, because you have 3 rows in compositor_biography...
perhaps you could go step by step,
first gathering the bio only:
SELECT compositors.id, compositors.name,
GROUP_CONCAT( compositor_biography.url SEPARATOR ';') AS concat_compositor_biography_url
FROM compositors
LEFT JOIN compositor_biography ON compositors.id = compositor_biography.compositor_id
GROUP BY compositors.id
then join the rest
select t.id, t.name,t.concat_compositor_biography_url,
GROUP_CONCAT( compositor_comments.comment SEPARATOR ';') AS concat_compositor_comments_comment
from (
SELECT compositors.id, compositors.name,
GROUP_CONCAT( compositor_biography.url SEPARATOR ';') AS concat_compositor_biography_url
FROM compositors
LEFT JOIN compositor_biography ON compositors.id = compositor_biography.compositor_id
GROUP BY compositors.id
) t
LEFT JOIN compositor_comments ON t.id = compositor_comments.compositor_id
and so on...
I don't see why it would not use the index unless the table is small.
Try 'explain select...' to confirm that.

Subqueries in SELECT clause

I need to SELECT from multiple tables to get a result table like the following:
+--------+-------+-------------------+----------------------+
| itemID | level | studyPhraseString | meaningPhraseStrings |
+--------+-------+-------------------+----------------------+
| 1 | 4 | la maison | house |
+--------+-------+-------------------+----------------------+
| 2 | 3 | voler | to fly,to steal |
+--------+-------+-------------------+----------------------+
Note: studyPhraseString and meaningPhraseStrings should be concatenated strings made up of values from the word table.
My tables:
item
CREATE TABLE `item` (
`itemID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`groupID` bigint(11) unsigned NOT NULL,
`studyLang` varchar(5) NOT NULL,
`meaningLang` varchar(5) NOT NULL,
`studyPhraseID` bigint(11) unsigned NOT NULL,
PRIMARY KEY (`itemID`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
meaning
CREATE TABLE `meaning` (
`meaningID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`itemID` bigint(11) unsigned NOT NULL,
`meaningPhraseID` bigint(11) unsigned NOT NULL,
`meaningIndex` int(11) unsigned NOT NULL,
PRIMARY KEY (`meaningID`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=latin1;
phrase
CREATE TABLE `phrase` (
`phraseID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`phraseLang` varchar(5) NOT NULL DEFAULT '',
PRIMARY KEY (`phraseID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1;
phrase_word
CREATE TABLE `phrase_word` (
`phrase_wordID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`phraseID` bigint(11) unsigned NOT NULL,
`wordID` bigint(11) unsigned NOT NULL,
`wordIndex` int(11) unsigned NOT NULL,
PRIMARY KEY (`phrase_wordID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1;
status
CREATE TABLE `status` (
`statusID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`itemID` bigint(11) unsigned NOT NULL,
`level` tinyint(1) unsigned NOT NULL DEFAULT '0',
`nextReviewTime` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`statusID`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
word
CREATE TABLE `word` (
`wordID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`string` varchar(64) NOT NULL DEFAULT '',
PRIMARY KEY (`wordID`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
I have written the following SELECT statement:
SELECT item.itemID, status.level,
(SELECT GROUP_CONCAT(word.string ORDER BY phrase_word.wordIndex SEPARATOR ' ')
FROM word INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
INNER JOIN item AS subItem ON phrase_word.phraseID=subItem.studyPhraseID
WHERE subItem.itemID=item.itemID
GROUP BY subItem.itemID
) AS studyPhraseString
FROM item INNER JOIN status ON item.itemID=status.itemID
WHERE item.groupID=5
ORDER BY status.statusID DESC
This works but does not include the meaningPhraseString. I can’t figure out how to concat the words into phrases AND concat the phrases into one string separated by ,
I have tried nested GROUP_CONCAT clauses with no success (subquery returns more than 1 row):
The question:
How should this statement be written to include meaningPhraseStrings? Thanks in advance.
PS: I'd like this to be a single query
I have tried the following but it fails. Why? It has two levels of correlated queries.
SELECT
item.itemID,
status.level,
(
SELECT GROUP_CONCAT(word.string ORDER BY phrase_word.wordIndex SEPARATOR ' ')
FROM word INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
INNER JOIN item AS subItem ON phrase_word.phraseID=subItem.studyPhraseID
WHERE subItem.itemID=item.itemID
GROUP BY subItem.itemID
) AS studyPhraseString,
(
SELECT GROUP_CONCAT(meaningPhraseString SEPARATOR '.')
FROM (
(
SELECT GROUP_CONCAT(word.string ORDER BY phrase_word.wordIndex SEPARATOR ' ') AS meaningPhraseString
FROM word INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
INNER JOIN meaning ON phrase_word.phraseID=meaning.meaningPhraseID
INNER JOIN item AS subItem ON meaning.itemID=subItem.itemID
WHERE subItem.itemID=item.itemID #This fails
GROUP BY meaning.meaningPhraseID
) AS meaningPhraseString
)
) AS meaningPhraseStrings
FROM item INNER JOIN status ON item.itemID=status.itemID
WHERE item.groupID=5
ORDER BY item.itemID DESC
Sample data:
INSERT INTO `status` VALUES (22,22,0,0),(23,23,0,0),(24,25,0,0),(25,24,0,0),(26,26,0,0);
INSERT INTO `item` VALUES (22,5,'fr','en',49),(23,5,'fr','en',48),(24,5,'fr','en',56),(25,5,'fr','en',50),(26,5,'fr','en',57);
INSERT INTO `meaning` VALUES (27,22,51,0),(28,23,52,0),(29,23,54,1),(30,24,59,0),(31,24,61,1),(32,25,53,0),(33,25,55,1),(34,26,58,0),(35,26,60,1);
INSERT INTO `phrase` VALUES (48,'fr'),(49,'fr'),(50,'fr'),(51,'en'),(52,'en'),(53,'en'),(54,'en'),(55,'en'),(56,'fr'),(57,'fr'),(58,'en'),(59,'en'),(60,'en'),(61,'en');
INSERT INTO `word` VALUES (46,'l\'autobus'),(47,'bus'),(48,'pourquoi'),(49,'comment'),(50,'why'),(51,'ça'),(52,'va?'),(53,'voler'),(54,'incroyable'),(55,'how'),(56,'is'),(57,'to'),(58,'are'),(59,'incredible'),(60,'that?'),(61,'you?'),(62,'fly'),(63,'amazing'),(64,'hi'),(65,'steal');
INSERT INTO `phrase_word` VALUES (86,49,46,0),(87,51,47,0),(88,48,48,0),(89,50,49,0),(90,52,50,0),(91,54,50,0),(92,50,51,1),(93,50,52,2),(94,57,53,0),(95,53,55,0),(96,56,54,0),(97,54,56,1),(98,53,58,1),(99,58,57,0),(100,59,59,0),(101,54,60,2),(102,53,61,2),(103,58,62,1),(104,61,63,0),(105,60,57,0),(106,55,64,0),(107,60,65,1);
Final answer:
SELECT i.itemID,
s.level,
sp.studyPhraseString,
GROUP_CONCAT(mp.meaningPhraseString
SEPARATOR ', ') AS meaningPhraseStrings
FROM item AS i
JOIN meaning AS m ON i.itemID = m.itemID
JOIN status AS s ON i.itemID = s.itemID
JOIN (
SELECT subItem.studyPhraseID,
GROUP_CONCAT(word.string
ORDER BY phrase_word.wordIndex
SEPARATOR ' ') AS studyPhraseString
FROM word
JOIN phrase_word
ON word.wordID=phrase_word.wordID
JOIN item AS subItem
ON phrase_word.phraseID=subItem.studyPhraseID
GROUP BY subItem.studyPhraseID
) AS sp ON i.studyPhraseID = sp.studyPhraseID
JOIN (
SELECT meaning.meaningPhraseID,
GROUP_CONCAT(word.string
ORDER BY phrase_word.wordIndex
SEPARATOR ' ') AS meaningPhraseString
FROM word
JOIN phrase_word ON word.wordID=phrase_word.wordID
JOIN meaning ON phrase_word.phraseID=meaning.meaningPhraseID
JOIN item AS subItem ON meaning.itemID=subItem.itemID
GROUP BY meaning.meaningPhraseID
) AS mp ON m.meaningPhraseID = mp.meaningPhraseID
GROUP BY i.itemID, s.level, sp.studyPhraseString
ORDER BY i.itemID, s.level, sp.studyPhraseString
Your question seems to be this:
how to concat the words into phrases AND concat the phrases into one string
Let's break it down. You need to join together five tables. Three of them are physical tables, namely item, meaning, and status. From those tables you get the references to the result set items you need called itemID and level, and you get the relationship between items and their meanings.
The other two tables you need are virtual tables (that is, subqueries). One of these gives you your French language phrases, and the other gives you your English-language translations.
Let us create the two queries for the virtual tables. Let's put the words into phrases first. A query like this achieves that goal.
SELECT subItem.studyPhraseID,
GROUP_CONCAT(word.string
ORDER BY phrase_word.wordIndex
SEPARATOR ' ') AS studyPhraseString
FROM word
INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
INNER JOIN item AS subItem ON phrase_word.phraseID=subItem.studyPhraseID
GROUP BY subItem.studyPhraseID
This gives you a resultset table of phrase ID numbers and the phrases. Here's a SQL fiddle for this one based on your samples. http://sqlfiddle.com/#!2/11ae2/9/0
Then, create a similar query giving you the meaningPhraseString values.
SELECT meaning.meaningPhraseID,
GROUP_CONCAT(word.string
ORDER BY phrase_word.wordIndex
SEPARATOR ' ') AS meaningPhraseString
FROM word
INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
INNER JOIN meaning ON phrase_word.phraseID=meaning.meaningPhraseID
INNER JOIN item AS subItem ON meaning.itemID=subItem.itemID
GROUP BY meaning.meaningPhraseID
This gives a list of ids and meaning phrases. Here's the fiddle. http://sqlfiddle.com/#!2/11ae2/6/0
So, we're going to need a five-way join (three physical tables and two subqueries) to get to our final result set. In summary, it will look like this:
SELECT i.itemID,
s.level,
sp.studyPhraseString,
mp.meaningPhraseString
FROM item AS i
JOIN meaning AS m ON i.itemID = m.itemID
JOIN status AS s ON i.itemID = s.itemID
JOIN (
/* the studyPhrase subquery */
) AS sp ON i.studyPhraseID = sp.studyPhraseID
JOIN (
/* the meaningPhrase subquery */
) AS mp ON m.meaningPhraseID = mp.meaningPhraseID
The trick here is that you can use a query (or virtual table) and a physical table interchangeably. So when you need to summarize a bunch of tables, you create a query to do that and then paste it into JOIN (/*query*/) AS alias.
Finally, you need to create the comma-joined strings (e.g. to fly, to steal) by adding yet another GROUP_CONCAT() and GROUP BY to your query. The end result is then
SELECT i.itemID,
s.level,
sp.studyPhraseString,
GROUP_CONCAT(mp.meaningPhraseString
SEPARATOR ', ') AS meaningPhraseStrings
FROM item AS i
JOIN meaning AS m ON i.itemID = m.itemID
JOIN status AS s ON i.itemID = s.itemID
JOIN (
/* the studyPhrase subquery */
) AS sp ON i.studyPhraseID = sp.studyPhraseID
JOIN (
/* the meaningPhrase subquery */
) AS mp ON m.meaningPhraseID = mp.meaningPhraseID
GROUP BY i.itemID, s.level, sp.studyPhraseString
ORDER BY i.itemID, s.level, sp.studyPhraseString
And that is your query. http://sqlfiddle.com/#!2/11ae2/16/0 It definitely takes advantage of the Structured in Structured Query Language.

Using GROUP_CONCAT in Mysql Queries

I have five tables in my database that I am working with. Everything works well till I start selecting states from the database using group concat.
These are my tables:
main_jobs
sub_jobs
category
state
state_job_relationship
Snippet of my table creation
create table if not exists category (
id int(3) not null auto_increment,
parent int(5) not null,
name varchar(255) not null,
description text,
slug varchar(255),
level int (3),
PRIMARY KEY (id, slug));
create table if not exists state (
id int(4) not null auto_increment,
country_id int(11) not null,
name varchar(255) not null,
PRIMARY KEY (id),
FOREIGN KEY (country_id) REFERENCES country (id))";
create table if not exists state_job_relationship (
id int(4) not null auto_increment,
state_id int(4) not null, FOREIGN KEY (state_id) REFERENCES state (id),
job_id int(11) not null, FOREIGN KEY (job_id) REFERENCES sub_jobs (id),
PRIMARY KEY (id));
create table if not exists main_jobs (
id int not null auto_increment, primary key(id),
company_name varchar(255),
job_title varchar(255));
create table if not exists sub_jobs (
id int not null auto_increment, primary key(id),
parent_id int(11) not null, FOREIGN KEY (parent_id) REFERENCES main_jobs (id),
title varchar(255),
description text not null,
category int (3), FOREIGN KEY (category) REFERENCES category (id)
);
This is what I want to select:
The job titles from the sub jobs tables, with the corresponding company name and other details from main_jobs and category table.
All worked well till I needed to select the states each sub_job falls in this format: State1, State2, State3
select sub_jobs.id as kid, main_jobs.id as parentid,
company_name, sub_jobs.description, sub_jobs.title,
job_title, category.name as ind,
DATE_FORMAT($column_date,'%a, %e %b %Y %T') as d,
(select group_concat(name seperator ', ')
from state_job_relationship, state
where job_id= kid
and state.id=state_job_relationship.state_id
group by state.id) states
from sub_jobs, category, main_jobs
where category.id=sub_jobs.category
and main_jobs.id=sub_jobs.parent_id
order by featured desc , $table.id desc
limit 100;
Tried the query above but it displays this error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'seperator ', ') from state_job_relationship, state where job_id= kid and state.i' at line 1
Please what am I not getting right?
It looks like your error is because you are using the alais kid inside of the correlated subquery. You should replace the kid with the sub_jobs.id.
Also you have seperator spelled incorrectly.
So your code will be:
select s.id as kid,
m.id as parentid,
company_name,
s.description,
s.title,
job_title,
c.name as ind,
DATE_FORMAT($column_date,'%a, %e %b %Y %T') as d,
(select group_concat(name SEPARATOR ', ')
from state_job_relationship
inner join state
on state.id=state_job_relationship.state_id
where job_id= s.id
group by state.id) states
from sub_jobs s
inner join category c
on c.id=s.category
inner join main_jobs m
on m.id=s.parent_id
order by featured desc , $table.id desc
limit 100;
You will notice that I changed your syntax to use ANSI JOIN syntax and I am using aliasas for the tables.
The other suggestion that I have would be to use a subquery and join to the subquery:
select s.id as kid,
m.id as parentid,
company_name,
s.description,
s.title,
job_title,
c.name as ind,
DATE_FORMAT($column_date,'%a, %e %b %Y %T') as d,
st.states
from sub_jobs s
inner join category c
on c.id=s.category
inner join main_jobs m
on m.id=s.parent_id
left join
(
select job_id, state.id, group_concat(name SEPARATOR ', ') states
from state_job_relationship
inner join state
on state.id=state_job_relationship.state_id
group by state.id, job_id
) st
on st.job_id= s.id
order by featured desc , $table.id desc
limit 100;