MySQL Lowercase Returned Value Or Entire Result - mysql

I have the below SQL query and it will return a group_name along with a list of departments for that group. I was able to lowercase the departments returned, but I can't figure out how to lowercase the group name as well.
Also, instead of lowercasing each returned column is there perhaps a way to lowercase the entire result in one swoop?
SELECT sg.group_name,A.dept_name
FROM `sys_groups` `sg`
INNER JOIN (SELECT gda.group_id,
GROUP_CONCAT(LOWER(sd.dept_name) ORDER BY `dept_name`
SEPARATOR '|'
) `dept_name`
FROM `group_dept_access` `gda`
INNER JOIN `sys_department` `sd`
ON gda.dept_id = sd.dept_id
GROUP BY gda.group_id) AS `A`
ON sg.group_id = A.group_id
Thank you in advance!

Try this:
SELECT LOWER(sg.group_name) group_name, LOWER(A.dept_name) dept_name
FROM sys_groups sg
INNER JOIN (SELECT gda.group_id,
GROUP_CONCAT(sd.dept_name ORDER BY dept_name SEPARATOR '|') dept_name
FROM group_dept_access gda
INNER JOIN sys_department sd ON gda.dept_id = sd.dept_id
GROUP BY gda.group_id
) AS A ON sg.group_id = A.group_id

Related

Group concat Query Performance

I have one query which almost took 22 second to populate data.
SELECT
TP.*,
GROUP_CONCAT(DISTINCT TS.`subject_name`) AS subjects,
GROUP_CONCAT(DISTINCT TC.`class_name`) AS classes,
GROUP_CONCAT(DISTINCT TT.`tution_name`) AS tution_type,
GROUP_CONCAT(DISTINCT TL.`name`) AS locations
FROM `tutor_profile` TP
LEFT JOIN `tutor_to_subject` TTS ON TP.`tutor_id`=TTS.`tutor`
LEFT JOIN `tutor_subjects` TS ON TS.`subject_id`=TTS.`subject`
LEFT JOIN `tutor_to_class` TTC ON TP.`tutor_id`=TTC.`tutor`
LEFT JOIN `tutor_classes` TC ON TC.`class_id`=TTC.`class`
LEFT JOIN `tutor_to_tution_type` TTTT ON TP.`tutor_id`=TTTT.`tutor`
LEFT JOIN `tution_types` TT ON TT.`tution_id`=TTTT.`tution_type`
LEFT JOIN `tutor_to_locality` TTL ON TP.`tutor_id`=TTL.`tutor`
LEFT JOIN `tutor_locality` TL ON TL.`id`=TTL.`locality`
WHERE 1=1 AND TP.`status` = 1
GROUP BY TP.`tutor_id`
ORDER BY TP.`date_added` DESC LIMIT 0 , 25
is there anyway to improve its performance?
You may try to use correlated subqueries in the output list instead of JOIN and GROUP BY:
SELECT
TP.*,
( SELECT GROUP_CONCAT(DISTINCT TS.`subject_name`)
FROM `tutor_to_subject` TTS
LEFT JOIN `tutor_subjects` TS ON TS.`subject_id`=TTS.`subject`
WHERE TP.`tutor_id`=TTS.`tutor` ) AS subjects,
-- the same for another output columns
FROM `tutor_profile` TP
WHERE 1=1 AND TP.`status` = 1
ORDER BY TP.`date_added` DESC LIMIT 0 , 25
Replace these
GROUP_CONCAT(DISTINCT TS.`subject_name`) AS subjects,
LEFT JOIN `tutor_to_subject` TTS ON TP.`tutor_id`=TTS.`tutor`
LEFT JOIN `tutor_subjects` TS ON TS.`subject_id`=TTS.`subject`
with
( SELECT GROUP_CONCAT(DISTINCT TS.`subject_name`
FROM `tutor_to_subject` TTS
JOIN `tutor_subjects` TS ON TS.`subject_id` = TTS.`subject`
WHERE TP.`tutor_id` = TTS.`tutor`
) AS subjects,
and toss the JOIN for TS. Do likewise for the other 3 group_concat + Left join.
For many-to-many tables, improve the indexes:
CREATE TABLE tutor_to_subject
tutor_id ...,
subject_id ...,
PRIMARY KEY(tutor_id, subject_id),
INDEX(subject_id)
) ENGINE=InnoDB;
More discussion: Many:many mapping
Also, get rid of the GROUP BY since it should not longer be needed.
And add
INDEX(status, date_added)

Combine data with same id in one column

I have this problem. I need to combine data with the same id in one column. The data are separated with ','. I use group_concat but the result is that it combines all. What I need is for example:
But the result is:
my code
SELECT A.bookId,A.bookDate, A.serviceDate, A.bookTime,A.status, GROUP_CONCAT(C.serviceItemId
SEPARATOR ',') AS Servis, B.custId, B.custFname, B.custLname,B.custContact, B.custEmail, B.gender
FROM booking A
JOIN booking_service C on A.bookId = C.bookId
JOIN customer B ON B.custId =
A.cust_fk WHERE A.cust_fk = 4
ORDER BY A.bookId
Can someone help me?
Why not group_concat the values in a sub-query and then join it to your main result set:
SELECT A.bookId,
A.bookDate,
A.serviceDate,
A.bookTime,
A.status,
d.Servis,
B.custId,
B.custFname,
B.custLname,
B.custContact,
B.custEmail,
B.gender
FROM (
SELECT bookId,
GROUP_CONCAT(serviceItemId SEPARATOR ',') AS Servis
FROM booking_service
GROUP BY bookId
) d
JOIN booking A ON a.bookID = d.bookID
JOIN booking_service C on A.bookId = C.bookId
JOIN customer B ON B.custId = A.cust_fk WHERE A.cust_fk = 4
ORDER BY A.bookId
You need to use GROUP BY with GROUP_CONCAT if you want to limit the results to, for example, a single bookId.

Sql trouble with coalesce() not working propely

i have a query and i'm having trouble to change the name of the last row of columb name to 'TOTAL'. The result gives me the same name of the row above the last row.
Here's my query:
SELECT COALESCE(ticket_types.name,'TOTAL') AS name,
COUNT(1) AS quantity
FROM tr_logs
LEFT JOIN tickets ON tr_logs.value = tickets.id
LEFT JOIN ticket_types ON tickets.ticket_type_id = ticket_types.id
LEFT JOIN transactions ON tr_logs.transaction_id = transactions.id
LEFT JOIN tr_fields_data AS tfd_shipping ON tfd_shipping.transaction_id = transactions.id
WHERE type = 'ADDITEM'
AND transactions.event_id = '46'
AND DATE(tr_logs.created_date)
BETWEEN '2017-03-26' AND '2017-05-24'
AND tfd_shipping.data IN ('0','570','571','771')
AND name IS NOT NULL
GROUP BY ticket_types.id WITH ROLLUP
The result looks like this:
name quantity
premium 56
outlaw 6
outlaw 62
Last row name from rollup is not null.... I need it to be TOTAL and not outlaw
Thanks
You haven't changed the name to TOTAL at all: you've changed the name of the column to name, and you've told it to replace any null values with TOTAL.
If you want to change the name of ticket_types.name to total, you just want
SELECT ticket_types.name AS total ...
(But it would be weird to rename something called name to total, so perhaps you need to clarify your requirements a little.)
This may or not be related to your observed problem, but the WHERE and GROUP BY clauses turn all the outer joins into inner joins. You should simplify the query to:
SELECT COALESCE(tt.name, 'TOTAL') AS name, COUNT(1) AS quantity
FROM tr_logs l JOIN
tickets
ON l.value = t.id JOIN
ticket_types tt
ON t.ticket_type_id = tt.id JOIN
transactions tr
ON l.transaction_id = tr.id JOIN
tr_fields_data fd
ON fd.transaction_id = tr.id
WHERE type = 'ADDITEM' AND
tr.event_id = '46' AND
DATE(l.created_date) BETWEEN '2017-03-26' AND '2017-05-24' AND
fd.data IN ('0', '570', '571', '771') AND
tt.name IS NOT NULL
GROUP BY tt.id WITH ROLLUP
Thanks to Gordon Linoff I have figure out my problem.
The name of the last row was never null beacause i GROUP BY with a different attribute.
Here's the solution.
SELECT COALESCE(tckn,'TOTAL') AS name, quantity FROM
(SELECT tt.name AS tckn, COUNT(1) AS quantity
FROM tr_logs AS l
LEFT JOIN tickets AS t ON l.value = t.id
LEFT JOIN ticket_types AS tt ON t.ticket_type_id = tt.id
LEFT JOIN transactions AS tr ON l.transaction_id = tr.id
LEFT JOIN tr_fields_data AS tfd ON tfd.transaction_id = tr.id
WHERE type = 'ADDITEM'
AND tr.event_id = '46'
AND DATE(l.created_date)
BETWEEN '2017-03-26' AND '2017-05-24'
AND tfd.data IN ('0','570','571','771')
GROUP BY tckn WITH ROLLUP) as sum;

MySQL query with GROUP BY and JOIN

Good afternoon,
I'm trying to get some information from my MySQL database and I'm having problems because I'm not able to have the information needed. I have tried a lot of different approaches and none of them have worked. I hope you can find something because I'm very close to find the solution but something is missing:
MySQL query:
SELECT b.id, b.tipo_perfil, round(avg(b.edad)), COUNT(c.zona), c.zona
FROM analizador_datos_usuario AS a
INNER JOIN analizador_datos_perfil AS b ON (a.id_usuario = b.id_perfil)
INNER JOIN analizador_datos_perfil_historial AS c ON (b.id = c.id_perfil)
WHERE a.id_usuario=21
GROUP BY b.tipo_perfil, c.zona
ORDER BY b.tipo_perfil ASC, count(c.zona) DESC
This query gives me the following information:
Table (in red it's what I need):
Kind regards,
try that :
SELECT b.tipo_perfil, round(avg(b.edad)), COUNT(distinct c.zona), group_concat(distinct b.id separator ' ') as id_list, group_concat(distinct c.zona separator ' ') as zona_list
FROM analizador_datos_usuario AS a
INNER JOIN analizador_datos_perfil AS b ON (a.id_usuario = b.id_perfil)
INNER JOIN analizador_datos_perfil_historial AS c ON (b.id = c.id_perfil)
WHERE a.id_usuario=21
GROUP BY b.tipo_perfil
ORDER BY b.tipo_perfil ASC, count(distinct c.zona) DESC
I think you are getting result what is displayed and you want result which is in red colour.
Try this modified query:-
SELECT b.id, b.tipo_perfil, round(avg(b.edad)), COUNT(c.zona) counted_zone, c.zona
FROM analizador_datos_usuario AS a
INNER JOIN analizador_datos_perfil AS b ON (a.id_usuario = b.id_perfil)
INNER JOIN analizador_datos_perfil_historial AS c ON (b.id = c.id_perfil)
WHERE a.id_usuario=21
GROUP BY b.tipo_perfil, c.zona
Having MAX(counted_zone)
ORDER BY b.tipo_perfil ASC, counted_zone DESC

MySQL Filter result again

The goal here is to:
1. Fetch the row with the most recent date from EACH store for EACH ingredient.
2. From this result, compare the prices to find the cheapest store for EACH ingredient.
I can accomplish either the first or second goal in separate queries, but not in the same.
How can i filter out a selection and then apply another filter on the previous result?
EDIT:
I've been having problems with results that i get from MAX and MIN since it just fetches the rest of the data arbitrarily. To avoid this im supposed to join tables on multiple columns (i guess). Im not sure how this will work with duplicate dates etc.
I've included an image of a query and its output data.
If we use ingredient1 as an example, it exists in three separate stores (in one store twice on different dates).
In this case the cheapest current price for ingredient1 would be store3. If the fourth row dated 2013-05-25 was even cheaper, it would still not "win" due to it being out of date.
(Disregard brandname, they dont really matter in this problem.)
Would appreciate any help/input you can offer!
This question is really interesting!
So, first, we get the row with the most recent date from EACH store for EACH ingredient. (It is possible that the most recent dates from each store can be different.)
Then, we compare the prices from each store (regardless of the date) to find the least price for each ingredient.
The query below uses the GROUP_CONCAT function in good measure. Here's a SO question regarding the use of the function.
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
, SUBSTRING_INDEX(
GROUP_CONCAT(b.name ORDER BY store_price.price),
',',
1
) as brand_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, btip.brand_id as brand_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
JOIN brand_to_ingredient_price btip ON ip.ingredient_price_id = btip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
JOIN brand b ON b.brand_id = store_price.brand_id
GROUP BY
store_price.ingredient_id;
You can check the implementation on this SQL Fiddle.
The version below, which ignores the brand, is slightly smaller:
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
GROUP BY
store_price.ingredient_id;
References:
Simulating First/Last aggregate functions in MySQL
This probably needs a couple of sub queries joined together.
This isn't tested (as I don't have your table definitions, nor any test data), but something like this:-
SELECT i.name AS ingredient,
ip.price,
ip.date,
s.name AS storename,
b.name AS brandname
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN store s
ON store_to_ingredient_price.store_id = store.store_id
INNER JOIN brand_to_ingredient_price btip
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
INNER JOIN brand b
ON brand_to_ingredient_price.brand_id = brand.brand_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
ip.date,
MIN(ip.price) AS lowest_price
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
MAX(ip.date) AS latest_date
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
GROUP BY ingredient_id, store_id
) Sub1
ON i.ingredient_id = Sub1.ingredient_id
AND stip.store_id = Sub1.store_id
AND ip.date = Sub1.latest_date
GROUP BY i.ingredient_id, stip.store_id, ip.date
) Sub2
ON i.ingredient_id = Sub2.ingredient_id
AND stip.store_id = Sub2.store_id
AND ip.date = Sub2.date
AND ip.price = Sub2.lowest_price
Try this:
SELECT `newest`.ingredient, `newest`.store,
`newest`.brand, `newest`.price, `newest`.`latest_date`
FROM
(SELECT ingredient.name AS ingredient, store.name AS store,
brand.name AS brand, ingredient_price.price,
MAX( ingredient_price.date ) AS `latest_date`
FROM ingredient
LEFT OUTER JOIN ingredient_price
ON ingredient.ingredient_id = ingredient_price.ingredient_id
LEFT OUTER JOIN store_to_ingredient_price
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN store
ON store_to_ingredient_price.store_id = store.store_id
LEFT OUTER JOIN brand_to_ingredient_price
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN brand
ON brand_to_ingredient_price.brand_id = brand.brand_id
GROUP BY ingredient.name) `newest`
ORDER BY `newest`.price
LIMIT 1