MYSQL results order is mixing - mysql

SELECT
GROUP_CONCAT(CONCAT(user_firstname,' ', user_lastname)) fullname,
workour_day,
GROUP_CONCAT(distinct timetable_start) starttime,
GROUP_CONCAT(timetable_end) endtime
FROM doctors_timetable
INNER JOIN doctors ON user_id = doctor_id
INNER JOIN workours ON timetable_day = workour_id GROUP BY workour_day
ORDER BY timetable_id ASC
before Thursday everything works good and than it's mixing:
http://s24.postimg.org/w827dxclh/Capture.png
MYSQL results order is mixing. How can I fix it?

As what i understand from your comment you need the starttime and endtitme values to be in ascending order if thats the case you can use ORDER BY in GROUP_CONCAT to tell in what order to concatenate values
SELECT
GROUP_CONCAT(CONCAT(user_firstname,' ', user_lastname)) fullname,
workour_day,
GROUP_CONCAT(distinct timetable_start ORDER BY timetable_start ASC) starttime,
GROUP_CONCAT(timetable_end ORDER BY timetable_end) endtime
FROM doctors_timetable
INNER JOIN doctors ON user_id = doctor_id
INNER JOIN workours ON timetable_day = workour_id
GROUP BY workour_day
ORDER BY timetable_id ASC
Note you are using GROUP_CONCAT function which has a default limit of 1024 characters set by default,if your output exceeds this limit the result will be truncated to increase the limit you can see steps mentioned in the manual

timetable_id is still a mystery, but it's what you're ordering by. Order by starttime, endtime if you want to enforce ordering on those.

Related

How many order by columns allowed in mysql, similarly group by?

I am looking for how many number of columns I can use in "order by" clause, for e.g. I have a column NAME asc, START_DATE asc, SKU_GROUP asc and want to add SKU_NAME asc in order clause. I am currently using 1 group by but for curiosity how many can be used within MySQL?
SELECT pop.SUB_ELEMENT, pop.NAME, sub_element.LDESC AS SUB_NAME, DATE_FORMAT(journey_visits.START_DATE, '%b %d %Y %h:%i %p' ) AS START_DATE, visit_sku.IS_CHECK,visit_sku.TYPE AS `SKU_TYPE`,brand.LDESC AS `SKU_GROUP`,sku.LDESC AS `SKU_NAME`,sku.SKU AS `MATERIAL` FROM visit_sku
LEFT JOIN journey_visits ON journey_visits.VISIT_ID = visit_sku.VISIT_ID
LEFT JOIN pop ON journey_visits.POP_ID = pop.POPID
LEFT JOIN sub_element ON sub_element.SubElementID=pop.SUB_ELEMENT
LEFT JOIN sku ON visit_sku.SKU_ID = visit_sku.SKU_ID AND visit_sku.SKU_ID = sku.SKU
LEFT JOIN brand ON sku.brandid = brand.BRANDID
WHERE DATE(journey_visits.START_DATE) BETWEEN '2016-04-01' AND '2016-04-03'
ORDER BY NAME, START_DATE, SKU_NAME
There is no limit, you can order by or group by all columns in a result set, although the latter would be useless.

Is it possible to find out a value that is the most different with pure MySQL?

Lets say I have a list of url's and I want to find out the url that is the most unique. I mean which is appearing the fewest. Here is an example of the database:
3598 ('www.emp.de/blog/tag/fear-factory/',)
3599 ('www.emp.de/blog/tag/white-russian/',)
3600 ('www.emp.de/blog/musik/die-emp-plattenkiste-zum-07-august-2015/',)
3601 ('www.emp.de/Warenkorb/car_/',)
3602 ('www.emp.de/ter_dataprotection/',)
3603 ('hilfe.monster.de/my20/faq.aspx#help_1_211589',)
3604 ('jobs.monster.de/l-nordrhein-westfalen.aspx',)
3605 ('karriere-beratung.monster.de',)
3606 ('karriere-beratung.monster.de',)
In this case it should return jobs.monster.de or hilfe.monster.de. I only want one return value. Is that possible with pure mysql?
It should be some kind of counting of the main url before the ".de"
At this moment I do it this way:
con.execute("select url, date from urls_to_visit ORDER BY RANDOM() LIMIT 1")
You could join the table on itself where ID's are not identical and count those, Then order by descending order and limit to 1 result.
not checked.
SELECT COUNT(*) as hitcount,
SUBSTRING_INDEX(t1.`url`,'.',2) as url
FROM table t1
INNER JOIN table t2 ON
SUBSTRING_INDEX(t1.`url`,'.',2) = SUBSTRING_INDEX(t2.`url`,'.',2)
AND t1.id <> t2.id
GROUP BY SUBSTRING_INDEX(t1.`url`,'.',2)
ORDER BY hitcount ASC
LIMIT 1
EDIT
Just checked on this, and it doesn't quite work.
I came up with this alternative, which uses a subquery to group all the domains together and get a count.
SELECT subq.count as hitcount,SUBSTRING_INDEX(t1.`url`,'.',2) as domain
FROM hits t1
INNER JOIN
(SELECT COUNT(*) as count,
SUBSTRING_INDEX(`url`,'.',2) as domain
FROM hits GROUP BY SUBSTRING_INDEX(`url`,'.',2)
) subq
ON subq.domain = SUBSTRING_INDEX(t1.`url`,'.',2)
GROUP BY SUBSTRING_INDEX(t1.`url`,'.',2)
ORDER BY hitcount ASC
LIMIT 1
working fiddle
Given your sample data (ignoring the parentheses, because I have no idea what those are doing), this query should do what you want:
select substring_index(url, '.', 2) as domain, count(*) as cnt
from table t
group by substring_index(url, '.', 2)
order by cnt desc
limit 1;

Sorting before GROUP BY - not working

I need to perform a sort before the GROUP BY clause, but MySQL does not want to cooperate.
SELECT `article`, `date`, `aip`
FROM `official_prices`
WHERE `article` = 2003
GROUP BY `article`
ORDER BY `date` ASC
The row that should be picked is the one with the earliest date (2013-07-15) but instead it picks the date that comes first in table order. Changing to DESC does no difference.
First image shows both rows, ungrouped. Second image is them being grouped.
This table is being joined to by a main query, so (I think) any solutions involving LIMIT 1 won't be useful to me.
Full query:
SELECT `articles`.*, `official_prices`.`aip`
FROM `articles`
LEFT JOIN `official_prices`
ON (`official_prices`.`article` = `articles`.`id`)
GROUP BY `articles`.`id`, `official_prices`.`article`
ORDER BY `official_prices`.`date` ASC, `articles`.`name`
You can't use group by and order like that. The order will only apply to the complete record set being returned and not in the group itself. This will work:
select o1.*
from official_prices o1
inner join
(
SELECT `article`, min(`date`) as mdate
from `official_prices`
WHERE `article` = 2003
GROUP BY `article`
) o2 on o1.article = o2.article and o1.date = o2.mdate
What you are trying to do is simply incorrect. The ordering before the group by does not have a (guaranteed) effect on the results.
My guess is that you want to get the most recent date and aip for that date. Here is a better approach:
SELECT `article`, max(`date`),
substring_index(group_concat(`aip` order by date desc), ',', 1) as lastAip
FROM `official_prices`
WHERE `article` = 2003
GROUP BY `article`;
The only downside is that the group_concat() will convert any value to a string. If it is some other type (and a string poses problems), then convert it back to the desired type.
Actually, an even better approach is to skip the group by entirely, because you are already filtering down to one article:
select article, `date`, aip
from official_prices
where article = 2003
order by `date` desc
limit 1;
The first approach works for multiple articles.
EDIT:
Your full query is:
SELECT `articles`.*, `official_prices`.`aip`
FROM `articles` LEFT JOIN
`official_prices`
ON `official_prices`.`article` = `articles`.`id`
GROUP BY `articles`.`id`, `official_prices`.`article`
ORDER BY `official_prices`.`date` ASC, `articles`.`name`;
You are looking for more than one article, so the second approach won't work. So, use the first:
SELECT `articles`.*,
substring_index(group_concat(`official_prices`.`aip` order by `official_prices`.`date` desc),
',', 1) as lastAIP
FROM `articles` LEFT JOIN
`official_prices`
ON `official_prices`.`article` = `articles`.`id`
GROUP BY `articles`.`id`, `official_prices`.`article`
ORDER BY `articles`.`name`;

MySQL sort grouped data

I have tried to program a inbox that display messages in the order they were received and then by if they have been read or not, it seemed to work for a while, but not it doesn't. It may have only worked under certain circumstances maybe..
Anyway here is my query;
SELECT `id`, `from_userid`, `read`, max(sent) AS sent
FROM (`who_messages`)
WHERE `to_userid` = '41'
GROUP BY `from_userid`
ORDER BY `read` ASC, `sent` DESC
I believe the problem is that the messages are being grouped in the wrong order.. as the inbox is always showing as read, when new messages exist. I get the right time of the new messages, but I am guessing this because I selected max(sent).
Is my logic wrong? or can I sort and then group as all my efforts have resulted in 'Every derived table must have its own alias'
Setup an SQL Fiddle - here's the best I came up with. Basically I do the ordering first in a sub-query then group them afterwards. That seemed to work with the (limited) test data I entered.
SELECT *
FROM (SELECT id, from_userid, is_read, sent
FROM who_messages
WHERE to_userid = 41
ORDER BY from_userid ASC, is_read ASC) m
GROUP BY m.from_userid
ORDER BY m.is_read ASC, m.sent DESC
See the fiddle to play around: http://sqlfiddle.com/#!2/4f63d/8
You are selecting non-grouping fields in a grouped query. It is not guaranteed which record of the group will be returned, and ORDER BY is processed after GROUP BY.
Try this:
SELECT m.*
FROM (
SELECT DISTINCT from_userid
FROM who_messages
WHERE to_userid = 41
) md
JOIN who_messages m
ON m.id =
(
SELECT mi.id
FROM who_message mi
WHERE (mi.to_userid, mi.from_userid) = (41, md.from_userid)
ORDER BY
mi.sent DESC, mi.id DESC
LIMIT 1
)
Create an index on who_message (to_userid, from_userid, sent, id) for this to work fast.
Update
The above query will return the record for the last message from any given user (including its read status). If you want to check that you have any unread messages from the user, use this:
SELECT m.*, md.all_read
FROM (
SELECT from_userid, MIN(read) AS all_read
FROM who_messages
WHERE to_userid = 41
GROUP BY
from_userid
) md
JOIN who_messages m
ON m.id =
(
SELECT mi.id
FROM who_message mi
WHERE (mi.to_userid, mi.from_userid) = (41, md.from_userid)
ORDER BY
mi.sent DESC, mi.id DESC
LIMIT 1
)
For this to work fast, create an index on who_message (to_userid, from_userid, read) (in addition to the previous index).
As Quassnoi said, you are using a GROUP BY query and ordering on 'read' which is not an aggregate function. Therefore you can't be certain of the value used by the MySQL engine (usually the last of the group but...)
I would suggest writing your query this way, as it doesn't involve any subquery and has some many other performance-friendly usage:
SELECT
from_userid,
COUNT(*) AS nb_messages,
SUM(NOT is_read) AS nb_unread_messages,
MAX(sent) AS last_sent
FROM who_messages
WHERE to_userid = 41
GROUP BY from_userid
ORDER BY nb_unread_messages DESC, last_sent DESC;
(I used Andy Jones' fiddle schema: http://sqlfiddle.com/#!2/4f63d/8.
By the way, many thanks Andy, this site is great !)
Hope this help !
"inbox that display messages in the order they were received and then by if they have been read or not ... however it is suppose to be the latest message" - assumes read is a nullable date/time column, and messages are stored in the order they are sent (newer have larger id than older - autoid)
SELECT wm.id, wm.from_userid, (wm.read IS NULL) as unread, wm.sent
FROM (SELECT MAX(id) AS id FROM who_messages WHERE to_userid = '41' GROUP BY from_userid) sub
INNER JOIN who_messages wm ON sub.id = wm.id
ORDER BY wm.sent DESC, wm.read

How do I get more than one column from a SELECT subquery?

Here is my problem :
I have 3 tables : account, account_event and account_subscription
account contains details like : company_name, email, phone, ...
account_event contains following events : incoming calls, outgoing calls, visit, mail
I use account_subscription in this query to retrieve the "prospects" accounts. If the account does not have a subscription, it is a prospect.
What I am using right now is the following query, which is working fine :
SELECT `account`.*,
(SELECT event_date
FROM clients.account_event cae
WHERE cae.account_id = account.id
AND cae.event_type = 'visit'
AND cae.event_done = 'Y'
ORDER BY event_date DESC
LIMIT 1) last_visit_date
FROM (`clients`.`account`)
WHERE (SELECT count(*)
FROM clients.account_subscription cas
WHERE cas.account_id = account.id) = 0
ORDER BY `last_visit_date` DESC
You can see that it returns the last_visit_date.
I would like to modify my query to return the last event details (last contact). I need the event_date AND the event_type.
So I tried the following query which is NOT working because apparently I can't get more than one column from my select subquery.
SELECT `account`.*,
(SELECT event_date last_contact_date, event_type last_contact_type
FROM clients.account_event cae
WHERE cae.account_id = account.id
AND cae.event_done = 'Y'
ORDER BY event_date DESC
LIMIT 1)
FROM (`clients`.`account`)
WHERE (SELECT count(*)
FROM clients.account_subscription cas
WHERE cas.account_id = account.id) = 0
ORDER BY `last_visit_date` DESC
I tried a lot of solutions around joins but my problem is that I need to get the last event for each account.
Any ideas?
Thank you in advance.
Jerome
Get a PRIMARY KEY in a subquery and join the actual table on it:
SELECT a.*, ae.*
FROM account a
JOIN account_event ae
ON ae.id =
(
SELECT id
FROM account_event aei
WHERE aei.account_id = a.id
AND aei.event_done = 'Y'
ORDER BY
event_date DESC
LIMIT 1
)
WHERE a.id NOT IN
(
SELECT account_id
FROM account_subscription
)
ORDER BY
last_visit_date DESC
Try moving the subquery to from part and alias it; it will look as just another table and you'll be able to extract more than one column from it.