MySQL select group concat? - mysql

I am trying to do a select from two tables. I have two tables:
select * from igw41_users;
+-----+------------+----------+-----------------------------+--------------------------------------------------------------+-------+-----------+---------------------+---------------------+------------+--------------------------------------------------------------------------------------------------------------------------------+---------------------+------------+--------+------+--------------+
| id | name | username | email | password | block | sendEmail | registerDate | lastvisitDate | activation | params | lastResetTime | resetCount | otpKey | otep | requireReset |
17 John Doe AAAAAA nothing else is needed from this table.
select * from igw41_user_profiles;
+---------+----------------------+-----------------------+----------+
| user_id | profile_key | profile_value | ordering |
17 profile.account_type "2" 4
17 profile.postal_code "75055" 1
I need to get id (from igw41_users), username igw41_users , postal_code (from igw41_user_profiles) and account_type (from igw41_user_profiles). But the info I need is in profile_values for each igw41_user_profiles.user_id that matches igw41_users.id.
select id, username, GROUP_CONCAT(profile_value SEPARATOR ',')
from igw41_users
left join igw41_user_profiles on igw41_users.id = igw41_user_profiles.user_id and igw41_user_profiles.profile_key = 'profile.account_type'
group by username;
this gives me the account_type, but I can't figure out how to get the postal_code If I do a right join it gives me: ERROR 1066 (42000): Not unique table/alias: 'igw41_user_profiles'
This is what I tried to get both profile values:
select id, username, GROUP_CONCAT(profile_value SEPARATOR ',')
from igw41_users
left join igw41_user_profiles on igw41_users.id = igw41_user_profiles.user_id and igw41_user_profiles.profile_key = 'profile.account_type'
right join igw41_user_profiles on igw41_users.id = igw41_user_profiles.user_id and igw41_user_profiles.profile_key = 'profile.postal_code'
group by username;
Thanks in advance.

take and igw41_user_profiles.profile_key = 'profile.account_type' out of your first query
If you wish to test for both use in
and igw41_user_profiles.profile_key in('profile.account_type','profile.postal_code')

In common way when you need to join same table more then once you need to use aliases for each join:
select id, username, GROUP_CONCAT(profile_value SEPARATOR ',')
from igw41_users
left join igw41_user_profiles p1
on igw41_users.id = p1.user_id and
p1.profile_key = 'profile.account_type'
right join igw41_user_profiles p2
on igw41_users.id = p2.user_id and
p2.profile_key = 'profile.postal_code'
group by username;
In this case the second join is redundant:
select id, username, GROUP_CONCAT(profile_value SEPARATOR ',')
from igw41_users
left join igw41_user_profiles p1 on igw41_users.id = p1.user_id
where
p1.profile_key = 'profile.account_type' OR
p1.profile_key = 'profile.postal_code'
group by username;

Related

COnditional JOIN query in MySQL

I have two mysql tables
user:
|--------------------------------|
| id | name | type | ruser_type |
|--------------------------------|
| 1 | Admin | a | |
| 2 | | r | c |
|--------------------------------|
customer
|-------------------------|
| id | name | user_id |
|-------------------------|
| 1 | Sam | 2 |
|-------------------------|
If user.type is 'a' or 's', then its admin user whose name is in user table.
If user.type is 'r' and ruser_type is 'c', then its regular user which has a relation in customer table where customer.user_id = user.id
I want a query which would run a conditional join.
If user.type is 'a' or 's', then name would be fetched from user table.
If user.type is 'r' and and ruser_type is 'c', then name would be fetched from customer table with the JOIN condition customer.user_id = user.id.
For this, I have written a query like this:-
SELECT users.fname as adminFname, customers.fname as customerFname, users.type FROM users
LEFT JOIN customers ON (customers.user_id = users.id AND
(
(users.type = 'r' AND users.ruser_type = 'c')
OR users.type = 'a'
OR users.type = 's'
)
)
WHERE users.id = 1
Is there any possibility to optimize the query more?
Also, how can I write this query using Laravel eloquent?
FWIW, I find this marginally easier to read...
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT
JOIN customers c
ON c.user_id = u.id
WHERE u.id = 1
AND (
(u.type = 'r' AND u.ruser_type = 'c')
OR (u.type IN('a','s'))
)
I have written two sql query hope this will help you
SELECT CASE CU.type WHEN 'a' OR 's' THEN CU.name END AS name,
CASE WHEN CU.type = 'r' AND CU.ruser_type = 'c' THEN CR.name END AS cust_name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this,
name cust_name type
Sam r
Admin a
and i have wrote another query like this,
SELECT CASE WHEN CU.type = 'a' OR 's' THEN CU.name ELSE CR.name END AS name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this
name type
Sam r
Admin a
DB File Link
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=b02d931b68a4f70d8b4a84144c60a572
This will give you required result for all users:
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)
Add where condition as required.
You can even simplify it further to get common firstName column in output:
SELECT COALESCE(u.fname, c.fname) firstName, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)

SQL query on join table

I've the follow SQL schema:
+----------+
| products |
+----------+
| id |
| name |
+----------+
^ 8
|
v 1
+-------------+
| values |
+-------------+
| value |
| product_id |
| property_id |
+-------------+
^ 8
|
v 1
+------------+
| properties |
+------------+
| id |
| name |
+------------+
One product has many properties and a property belongs to many products. The values table is the join table for the many_to_many association between products and properties. And in this table is saved the value of the property for a product.
Now I'm looking for a query to select all products with property x with value a, and property y with value b ecc. My try is this query but return no records:
SELECT DISTINCT
products.*
FROM
products
INNER JOIN
product_values
ON product_values.product_id = products.id
INNER JOIN
properties
ON properties.id = product_values.property_id
WHERE
(properties.name = 'size' AND product_values.value = 'big')
AND (properties.name = 'color' AND product_values.value = 'red')
If possible I need a query with no nested select.
Since a property can not be color and size at the same time you need to use OR in your where clause. Then group the data and check if both are in the group with having
SELECT products.id, products.name
FROM `products`
INNER JOIN `product_values` ON `product_values`.`product_id` = `products`.`id`
INNER JOIN `properties` ON `properties`.`id` = `product_values`.`property_id`
WHERE (properties.name = 'size' AND product_values.value = 'big')
OR (properties.name = 'color' AND product_values.value = 'red')
GROUP BY products.id, products.name
HAVING count(distinct properties.name) = 2
I would do this using group by and having:
select pv.product_id
from product_values pv join
properties p
on pv.property_id = p.id
where (p.name, v.value) in ( ('size', 'big'), ('color', 'red') )
group by pv.product_id
having count(distinct p.name) = 2;
Another approach using sum to filter multiple attributes for an entity
SELECT
`p`.*
FROM
`products` p
INNER JOIN `product_values` v
ON `v`.`product_id` = `p`.`id`
INNER JOIN `properties` pr
ON `pr`.`id` = `v`.`property_id`
GROUP BY p.id
HAVING SUM (pr.name = 'size' AND v.value = 'big')
AND SUM(pr.name = 'color' AND v.value = 'red')

How to find exact match item with AND condition from left outer join

I have a MySQL table
Booktable
+--------+-------------+-----+
| bookno | bookname | ... |
+--------+-------------+-----+
| 1 | FINALFANTASY| ... |
+--------+-------------+-----+
Authortable
+--------+-------------+-----+
| bookno | Authorname | ... |
+--------+-------------+-----+
| 1 | SQUARE | ... |
+--------+-------------+-----+
| 1 | ENIX | ... |
+--------+-------------+-----+
so I would like to make a search condition to get the book that match with the result.
I try with
select b.bookname,a.authorname from booktable as b
left outer join authortable a on b.bookno = a.bookno
where a.authorname = "square" and a.authorname = "enix"
It only work with only one where condition.but when I try with two authorname there is no result found. what should I do ?
(this query it working with "OR" but not "AND" but I really want the value that match the search condition or if there are some search condition that not match but not blank it should not be showing(so or it not working in this case)
Use aggregation to identify which books have both the authors you want:
SELECT t1.bookname,
t2.authorname
FROM booktable t1
INNER JOIN authortable t2
ON t1.bookno = t2.bookno
INNER JOIN
(
SELECT bookno
FROM authortable
WHERE authorname IN ('square', 'enix')
GROUP BY bookno
HAVING COUNT(DISTINCT authorname) = 2
) t3
ON t1.bookno = t3.bookno
Demo here:
SQLFiddle
Tim Biegeleisen's answer is great, but in case you need exactly match, the the last SQL in the following is correct:
SELECT * FROM book;
SELECT * FROM author;
/* this SQL will return book's author name more than 2 also true */
SELECT b.bookname, a.authorname
FROM book AS b
JOIN author AS a ON b.bookno = a.bookno
JOIN (
SELECT bookno FROM author
WHERE authorname in ('SQUARE', 'ENIX')
GROUP BY 1
HAVING count(*) = 2
) AS a2 ON b.bookno = a2.bookno;
/* this sQL will return only 2 and all matched authors: */
SELECT b.bookname, a.authorname
FROM book AS b
JOIN author AS a ON b.bookno = a.bookno
JOIN (
SELECT bookno FROM author
WHERE authorname in ('SQUARE', 'ENIX')
GROUP BY 1
HAVING count(*) = 2
) AS a2 ON b.bookno = a2.bookno
JOIN (
SELECT bookno FROM author
GROUP BY 1
HAVING count(distinct authorname) = 2
) AS a3 ON b.bookno = a3.bookno
PS1 - no need left join
PS2 - no need count distinct - unless your author table not design properly
If title is FANTASY genre is Adventure,fantasy, and search condition is
[ADVENTURE] = found
[FANTASY] = found
[ADVENTURE,FANTASY] = found
[ADVENTURE,FANTASY,ACTION] = not found
Then the SQL will be:
SELECT b.bookname, a.authorname
FROM book AS b
JOIN author AS a ON b.bookno = a.bookno
JOIN author AS a1 ON b.bookno = a1.bookno AND a1.authorname = 'SQUARE'
JOIN author AS a2 ON b.bookno = a2.bookno AND a2.authorname = 'ENIX'
Above is working, and I m wondering if there is a performance improvement

Find duplicate row get only the older one

I have this query that will give me those rows that have the field "username" duplicated:
SELECT id, jos_users.username, email, password, lastvisitDate
FROM jos_users
INNER JOIN (SELECT username
FROM jos_users
GROUP BY username HAVING count(id) > 1) dup
ON jos_users.username = dup.username;
I need to get those that have the lastvisitDate lower.
For example:
id | username | email | password | lastvisitDate |
1 | mylogin | | | 2014-10-15 16:42:42 |
2 | mylogin | | | 2014-10-16 16:42:42 |
As you can see, the row with id=1 have the lowest lastvisitDate. How could I put this sentence on the query?
I want this because I'll peform a delete query using this select to delete duplicated rows.
SELECT id, jos_users.username, email, password, lastvisitDate
FROM jos_users
INNER JOIN (SELECT username
FROM jos_users jb
where jb.lastvisitDate =
(select min(jb1.lastvisitDate)
from jos_users jb1
where jb1.username = jb.username)
GROUP BY username
HAVING count(id) > 1) dup
ON jos_users.username = dup.username;
I would probably write this as:
SELECT u.id, u.username, u.email, u.password, u.lastvisitDate
FROM jos_users u
WHERE EXISTS (SELECT 1 FROM jos_users WHERE username = u.username GROUP BY username HAVING count(id) > 1)
AND EXISTS (SELECT 1 FROM jos_users WHERE username = u.username GROUP BY username HAVING min(lastvisitDate) = u.lastvisitDate)
You can get minimum last visit date and do deletion
DELETE J FROM jobusers J
INNER JOIN (
SELECT username, MIN(lastvisitDate) as lastVisitDate
FROM job_users
GROUP BY username HAVING count(id) > 1
)T
ON J.username = T.username
AND J.lastvisitDate = T.lastVisitDate

repeated values with group_concat and joins

i have a little problem with the following Query. It returns multiple values.
Here is the result I'm receiving:
+----+--------------+--------+--------+---------------------+----------+------------------+
| id | company | county | vm | os | products | sn |
+----+--------------+--------+--------+---------------------+----------+------------------+
| 1 | ABC Corp | USA | VMW | Linux, Linux, Linux | 3 | A123, B234, A343 |
| 2 | DEF Corp | USA | CIT | Windows | 1 | I223 |
+----+--------------+--------+--------+---------------------+----------+------------------+
As you can see, the first line shows 3 times Linux, but this should be only listed once. I've seen that this problem only occurs if the customer has more than 1 product. I think i have to Group my Query or something like this, but i don't know how.
Here is my Query:
SELECT
customer.id,
customer.company,
countries.en AS country,
vmenv.name AS vm,
GROUP_CONCAT(operatingsystems.name SEPARATOR ', ') AS os,
COUNT(device2customer.sn) AS products,
GROUP_CONCAT(device2customer.sn SEPARATOR ', ') AS sn
FROM
customer
LEFT JOIN
countries
ON
customer.country = countries.id
LEFT JOIN
vmenv2kunden
ON
vmenv2kunden.customerid = customer.id
LEFT JOIN
vmenv
ON
vmenv2kunden.vmenvnr = vmenv.id
LEFT JOIN
operatingsystems2customer
ON
operatingsystems2customer.customerid = customer.id
LEFT JOIN
operatingsystems
ON
operatingsystems2customer.osnr = operatingsystems.id
LEFT JOIN
device2customer
ON
device2customer.kundenid = customer.id
GROUP BY
customer.id
In your query change the group_concat statement to
GROUP_CONCAT(DISTINCT operatingsystems.name SEPARATOR ', ') AS os,
COUNT(DISTINCT device2customer.sn) AS products,
GROUP_CONCAT(DISTINCT device2customer.sn SEPARATOR ', ') AS sn
By adding the DISTINCT key word it should work for you
you just need yo use distinct in Group_concat.
Please try the below updated Query.
SELECT
customer.id,
customer.company,
countries.en AS country,
vmenv.name AS vm,
GROUP_CONCAT(distinct operatingsystems.name SEPARATOR ', ') AS os,
COUNT(device2customer.sn) AS products,
GROUP_CONCAT(device2customer.sn SEPARATOR ', ') AS sn
FROM
customer
LEFT JOIN
countries
ON
customer.country = countries.id
LEFT JOIN
vmenv2kunden
ON
vmenv2kunden.customerid = customer.id
LEFT JOIN
vmenv
ON
vmenv2kunden.vmenvnr = vmenv.id
LEFT JOIN
operatingsystems2customer
ON
operatingsystems2customer.customerid = customer.id
LEFT JOIN
operatingsystems
ON
operatingsystems2customer.osnr = operatingsystems.id
LEFT JOIN
device2customer
ON
device2customer.kundenid = customer.id
GROUP BY
customer.id