complex MySQL Order by not working - sql

Here is the select statement I'm using. The problem happens with the sorting. When it is like below, it only sorts by t2.userdb_user_first_name, doesn't matter if I put that first or second. When I remove that, it sorts just fine by the displayorder field value pair. So I know that part is working, but somehow the combination of the two causes the first_name to override it. What I want is for the records to be sorted by displayorder first, and then first_name within that.
SELECT t1.userdb_id
FROM default_en_userdbelements as t1
INNER JOIN default_en_userdb AS t2 ON t1.userdb_id = t2.userdb_id
WHERE t1.userdbelements_field_name = 'newproject'
AND t1.userdbelements_field_value = 'no'
AND t2.userdb_user_first_name!='Default'
ORDER BY
(t1.userdbelements_field_name = 'displayorder' AND t1.userdbelements_field_value),
t2.userdb_user_first_name;
Edit: here is what I want to accomplish. I want to list the users (that are not new projects) from the userdb table, along with the details about the users that is stored in userdbelements. And I want that to be sorted first by userdbelements.displayorder, then by userdb.first_name. I hope that makes sense? Thanks for the really quick help!
Edit: Sorry for disappearing, here is some sample data
userdbelements
userdbelements_id userdbelements_field_name userdbelements_field_value userdb_id
647 heat 1
648 displayorder 1 - Sponsored 1
645 condofees 1
userdb
userdb_id userdb_user_name userdb_emailaddress userdb_user_first_name userdb_user_last_name
10 harbourlights info#harbourlightscondosminium.ca Harbourlights 1237 Northshore Blvd, Burlington
11 harbourview info#harbourviewcondominium.ca Harbourview 415 Locust Street, Burlington
12 thebalmoral info#thebalmoralcondominium.ca The Balmoral 2075 & 2085 Amherst Heights Drive, Burlington

You are trying to use an invalid ORDER BY.
ORDER BY (t1.userdbelements_field_name = 'displayorder' AND t1.userdbelements_field_value)
It must reference a table column or returned aliased column.
I really cannot follow how this query would even be possible as you already have limited
t1.userdbelements_field_name = newproject and then you wish to order by the case of it being equal to displayorder.
Could you please modify your question to state exactly what it is that you are trying to accomplish in your order by clause?

From what I understand, you'd have to join to default_en_userdbelements solely for the displayorder value. However, I suspect there's something wrong with your query and that it probably returns duplicate values for userdb_id.
Perhaps you should say what you're trying to actually do, not explain the way you're trying to do it.
SELECT t1.userdb_id
FROM default_en_userdbelements AS t1
JOIN default_en_userdb AS t2 ON t1.userdb_id = t2.userdb_id
JOIN default_en_userdbelements AS o ON (o.userdb_id, o.userdbelements_field_name)
= (t1.userdb_id, 'displayorder')
WHERE t1.userdbelements_field_name = 'newproject'
AND t1.userdbelements_field_value = 'no'
AND t2.userdb_user_first_name != 'Default'
ORDER BY o.userdbelements_field_value,
t2.userdb_user_first_name

You could do something like this:
ORDER BY
(CASE WHEN t1.userdbelements_field_name = 'displayorder'
THEN t1.userdbelements_field_value
ELSE $some_large_number
END),
t2.userdb_user_first_name;
It sorts using the value of t1.userdbelements_field_value when t1.userdbelements_field_name = 'displayorder', but you have to supply some other value of the same type to apply for the ELSE.

Related

How to Group_Concat with a 3-table JOIN for genealogy

I am failing to grasp how I can get the following outcome. I thought perhaps via GROUP_CONCAT, but I am also joining on 3 tables, and unclear on the correct syntax or if this is even the best approach.
Generic table layout:
Table Users: user_id | first | last
Table Orgs org_id | org_name
Table Relationship user_id | org_id | start_year | end_year
The relationship table has MANY entries, that may be associated with that specific user_id.
I need to get the User columns: id, first, last. I'd like to try and group the org data into 1 concatenated, delimited field. Maybe a double group_concatenation is needed? Which would consist of the org_id, org_name, start_year & end_year for all records in the relationship table that match the user_id. I'm hoping for an output like this:
Each '|' represents a new column/piece of data.
If there was only 1 org_id associated with the user_id, the output would be (similar) to:
user_id | first | last | org_id-org_name-start_year-end_year
If there were more than 1 org found/associated with that user_id, the output would have more concatenated/delimited data in the same column:
user_id | first | last | org_id-org_name-start_year-end_year^org_id-org_name-start_year-end_year^org_id-org_name-start_year-end_year
(Notice the '-' delimiter between values and the '^' delimiter between new 'org-grouped' data.)
When I grab that data, I can then just break it up (on the backend/PHP side of things) into an array or whatever.
I'm not sure how I can GROUP_CONCAT (if that is even the best approach here?) while I have to JOIN on 3 separate tables.
This is not my REAL query. (I'm not sure if I should post it, as I do not want to cause any confusion as it does NOT match my dummy table/column names.)
I just wanted to show my attempt that gets me 3 individual rows, (using my JOINS) but no GROUP_CONCAT stuff:
SELECT genealogy_users.imis_id, genealogy_users.full_name,
genealogy_users.member_email, genealogy_orgs.org_id,
genealogy_orgs.org_name, genealogy_relations.user_id,
genealogy_relations.relation_type, genealogy_relations.start_year,
genealogy_relations.end_year
FROM genealogy_users
INNER JOIN genealogy_relations ON genealogy_users.imis_id = genealogy_relations.user_id
INNER JOIN genealogy_orgs ON genealogy_relations.org_id = genealogy_orgs.org_id
WHERE genealogy_users.imis_id = '00003';
UPDATE:
Well I seemed to have fudged my way through it. But I'm not sure how legit this is.
Its -ALMOST- there. I believe I still need a JOIN or something? Since the genealogy_orgs.org_id = '84864' is hardcoded, and it should NOT be. Maybe it needs to come from a JOIN or something?
SELECT genealogy_users.*,
(SELECT GROUP_CONCAT(org_id,'-',
(SELECT org_name FROM genealogy_orgs WHERE genealogy_orgs.org_id = '84864'),
'-',start_year,'-',end_year,'^')
FROM genealogy_relations WHERE genealogy_relations.user_id = genealogy_users.imis_id
) AS alumni_list
FROM genealogy_users
WHERE genealogy_users.imis_id = '00003';
UPDATE 2:
My final attempt, which I think is getting me what I need. (But it's late, and I'll check back tomorrow and look at things more closely.)
SELECT genealogy_users.imis_id, genealogy_users.full_name,
genealogy_users.member_email, genealogy_orgs.org_id,
genealogy_orgs.org_name, genealogy_relations.user_id,
genealogy_relations.relation_type, genealogy_relations.start_year,
genealogy_relations.end_year,
(SELECT GROUP_CONCAT(org_id,'-',org_name,'-',start_year,'-',end_year,'^')
FROM genealogy_relations
WHERE genealogy_relations.user_id = genealogy_users.imis_id
) AS alumni_list
FROM genealogy_users
INNER JOIN genealogy_relations ON genealogy_users.imis_id = genealogy_relations.user_id
INNER JOIN genealogy_orgs ON genealogy_relations.org_id = genealogy_orgs.org_id
WHERE genealogy_users.imis_id = '00003';
Is there anything to make note of in the above attempt? Or is there a better approach? Hopefully something easily readable so it makes sense?

how to join multiple tables without showing repeated data?

I pop into a problem recently, and Im sure its because of how I Join them.
this is my code:
select LP_Pending_Info.Service_Order,
LP_Pending_Info.Pending_Days,
LP_Pending_Info.Service_Type,
LP_Pending_Info.ASC_Code,
LP_Pending_Info.Model,
LP_Pending_Info.IN_OUT_WTY,
LP_Part_Codes.PartCode,
LP_PS_Codes.PS,
LP_Confirmation_Codes.SO_NO,
LP_Pending_Info.Engineer_Code
from LP_Pending_Info
join LP_Part_Codes
on LP_Pending_Info.Service_order = LP_Part_Codes.Service_order
join LP_PS_Codes
on LP_Pending_Info.Service_Order = LP_PS_Codes.Service_Order
join LP_Confirmation_Codes
on LP_Pending_Info.Service_Order = LP_Confirmation_Codes.Service_Order
order by LP_Pending_Info.Service_order, LP_Part_Codes.PartCode;
For every service order I have 5 part code maximum.
If the service order have only one value it show the result correctly but when it have more than one Part code the problem begin.
for example: this service order"4182134076" has only 2 part code, first'GH81-13601A' and second 'GH96-09938A' so it should show the data 2 time but it repeat it for 8 time. what seems to be the problem?
If your records were exactly the same the distinct keyword would have solved it.
However in rows 2 and 3 which have the same Service_Order and Part_Code if you check the SO_NO you see it is different - that is why distinct won't work here - the rows are not identical.
I say you have some problem in one of the conditions in your joins. The different data is in the SO_NO column so check the raw data in the LP_Confirmation_Codes table for that Service_Order:
select * from LP_Confirmation_Codes where Service_Order = 4182134076
I assume you are missing an and with the value from the LP_Part_Codes or LP_PS_Codes (but can't be sure without seeing those tables and data myself).
By this sentence If the service order have only one value it show the result correctly but when it have more than one Part code the problem begin. - probably you are missing and and with the LP_Part_Codes table
Based on your output result, here are the following data that caused multiple output.
Service Order: 4182134076 has :
2 PartCode which are GH81-13601A and GH96-09938A
2 PS which are U and P
2 SO_NO which are 1.00024e+09 and 1.00022e+09
Therefore 2^3 returns 8 rows. I believe that you need to check where you should join your tables.
Use DINTINCT
select distinct LP_Pending_Info.Service_Order,LP_Pending_Info.Pending_Days,
LP_Pending_Info.Service_Type,LP_Pending_Info.ASC_Code,LP_Pending_Info.Model,
LP_Pending_Info.IN_OUT_WTY, LP_Part_Codes.PartCode,LP_PS_Codes.PS,
LP_Confirmation_Codes.SO_NO,LP_Pending_Info.Engineer_Code
from LP_Pending_Info
join LP_Part_Codes on LP_Pending_Info.Service_order = LP_Part_Codes.Service_order
join LP_PS_Codes on LP_Part_Codes.Service_Order = LP_PS_Codes.Service_Order
join LP_Confirmation_Codes on LP_PS_Codes.Service_Order = LP_Confirmation_Codes.Service_Order
order by LP_Pending_Info.Service_order, LP_Part_Codes.PartCode;
distinct will not return duplicates based on your select. So if a row is same, it will only return once.

Count of how many times id occurs in table SQL regexp

Hi I have a redshift table of articles that has a field on it that can contain many accounts. So there is a one to many relationship between articles to accounts.
However I want to create a new view where it lists the partner id's in one column and in another column a count of how many times the partner id appears in the articles table.
I've attempted to do this using regex and created a new redshift view, but am getting weird results where it doesn't always build properly. So one day it will say a partner appears 15 times, then the next 17, then the next 15, when the partner id count hasn't actually changed.
Any help would be greatly appreciated.
SELECT partner_id,
COUNT(DISTINCT id)
FROM (SELECT id,
partner_ids,
SPLIT_PART(partner_ids,',',i) partner_id
FROM positron_articles a
LEFT JOIN util.seq_0_to_500 s
ON s.i < regexp_count (partner_ids,',') + 2
OR s.i = 1
WHERE i > 0
AND regexp_count (partner_ids,',') = 0
ORDER BY id)
GROUP BY 1;
Let's start with some of the more obvious things and see if we can start to glean other information.
Next GROUP BY 1 on your outer query needs to be GROUP BY partner_id.
Next you don't need an order by in your INNER query and the database engine will probably do a better job optimizing performance without it so remove ORDER BY id.
If you want your final results to be ordered then add an ORDER BY partner_id or similar clause after your group by of your OUTER query.
It looks like there are also problems with how you are splitting a partnerid from partnerids but I am not positive about that because I need to understand your view and the data it provides to know how that affects your record count for partnerid.
Next your LEFT JOIN statement on the util.seq_0_to_500 I am pretty sure you can drop off the s.i = 1 as the first condition will satisfy that as well because 2 is greater than 1. However your left join really acts more like an inner join because you then exclude any non matches from positron_articles that don't have a s.i > 0.
Oddly then your entire join and inner query gets kind of discarded because you only want articles that have no commas in their partnerids: regexp_count (partner_ids,',') = 0
I would suggest posting the code for your util.seq_0_to_500 and if you have a partner table let use know about that as well because you can probably get your answer a lot easier with that additional table depending on how regexp_count works. I suspect regex_count(partnerids,partnerid) exampleregex_count('12345,678',1234) will return greater than 0 at which point you have no choice but to split the delimited strings into another table before counting or building a new matching function.
If regex_count only matches exact between commas and you have a partner table your query could be as easy as this:
SELECT
p.partner_id
,COUNT(a.id) AS ArticlesAppearedIn
FROM
positron_articles a
LEFT JOIN PARTNERTABLE p
ON regexp_count(a.partnerids,p.partnerid) > 0
GROUP BY
p.partner_id
I will actually correct myself as I just thought of a way to join a partner table without regexp_count. So if you have a partner table this might work for you. If not you will need to split strings. It basically tests to see if the partnerid is the entire partnerids, at the beginning, in the middle, or at the end of partnerids. If one of those is met then the records is returned.
SELECT
p.partner_id
,COUNT(a.id) AS ArticlesAppearedIn
FROM
PARTNERTABLE p
INNER JOIN positron_articles a
ON
(
CASE
WHEN a.partnerids = CAST(p.partnerid AS VARCHAR(100)) THEN 1
WHEN a.partnerids LIKE p.partnerid + ',%' THEN 1
WHEN a.partnerids LIKE '%,' + p.partnerid + ',%' THEN 1
WHEN a.partnerids LIKE '%,' + p.partnerid THEN 1
ELSE 0
END
) = 1
GROUP BY
p.partner_id

Selecting rows from Parent Table only if multiple rows in Child Table match

Im building a code that learns tic tac toe, by saving info in a database.
I have two tables, Games(ID,Winner) and Turns(ID,Turn,GameID,Place,Shape).
I want to find parent by multiple child infos.
For Example:
SELECT GameID FROM Turns WHERE
GameID IN (WHEN Turn = 1 THEN Place = 1) AND GameID IN (WHEN Turn = 2 THEN Place = 4);
Is something like this possible?
Im using ms-access.
Turm - Game turn GameID - Game ID Place - Place on matrix
1=top right, 9=bottom left Shape - X or circle
Thanks in advance
This very simple query will do the trick in a single scan, and doesn't require you to violate First Normal Form by storing multiple values in a string (shudder).
SELECT T.GameID
FROM Turns AS T
WHERE
(T.Turn = 1 AND T.Place = 1)
OR (T.Turn = 2 AND T.Place = 4)
GROUP BY T.GameID
HAVING Count(*) = 2;
There is no need to join to determine this information, as is suggested by other answers.
Please use proper database design principles in your database, and don't violate First Normal Form by storing multiple values together in a single string!
The general solution to your problem can be accomplished by using a sub-query that contains a self-join between two instances of the Turns table:
SELECT * FROM Games
WHERE GameID IN
(
SELECT Turns1.GameID
FROM Turns AS Turns1
INNER JOIN Turns AS Turns2
ON Turns1.GameID = Turns2.GameID
WHERE (
(Turns1.Turn=1 AND Turns1.Place = 1)
AND
(Turns2.Turn=2 AND Turns2.Place = 4))
);
The Self Join between Turns (aliased Turns1 and Turns2) is key, because if you just try to apply both sets of conditions at once like this:
WHERE (
(Turns.Turn=1 AND Turns.Place = 1)
AND
(Turns.Turn=2 AND Turns.Place = 4))
you will never get any rows back. This is because in your table there is no way for an individual row to satisfy both conditions at the same time.
My experience using Access is that to do a complex query like this you have to use the SQL View and type the query in on your own, rather than use the Query Designer. It may be possible to do in the Designer, but it's always been far easier for me to write the code myself.
select GameID from Games g where exists (select * from turns t where
t.gameid = g.gameId and ((turn =1 and place = 1) or (turn =2 and place =5)))
This will select all the games that have atleast one turn with the coresponding criteria.
More info on exist:
http://www.techonthenet.com/sql/exists.php
I bypassed this problem by adding a column which holds the turns as a string example : "154728" and i search for it instead. I think this solution is also less demanding on the database

MySQL to return only last date / time record

We have a database that stores vehicle's gps position, date, time, vehicle identification, lat, long, speed, etc., every minute.
The following select pulls each vehicle position and info, but the problem is that returns the first record, and I need the last record (current position), based on date (datagps.Fecha) and time (datagps.Hora). This is the select:
SELECT configgps.Fichagps,
datacar.Ficha,
groups.Nombre,
datagps.Hora,
datagps.Fecha,
datagps.Velocidad,
datagps.Status,
datagps.Calleune,
datagps.Calletowo,
datagps.Temp,
datagps.Longitud,
datagps.Latitud,
datagps.Evento,
datagps.Direccion,
datagps.Provincia
FROM asigvehiculos
INNER JOIN datacar ON (asigvehiculos.Iddatacar = datacar.Id)
INNER JOIN configgps ON (datacar.Configgps = configgps.Id)
INNER JOIN clientdata ON (asigvehiculos.Idgroup = clientdata.group)
INNER JOIN groups ON (clientdata.group = groups.Id)
INNER JOIN datagps ON (configgps.Fichagps = datagps.Fichagps)
Group by Fichagps;
I need same result I'm getting, but instead of the older record I need the most recent
(LAST datagps.Fecha / datagps.Hora).
How can I accomplish this?
Add ORDER BY datagps.Fecha DESC, datagps.Hora DESC LIMIT 1 to your query.
I'm not sure why you are having any problems with this as Lex's answers seem good.
I would start putting ORDER BY's in your query so it puts them in an order, when it's showing the record you want as the first one in the list, then add the LIMIT.
If you want the most recent, then the following should be good enough:
ORDER BY datagps.Fecha DESC, datagps.Hora DESC
If you simply want the record that was added to the database most recently (irregardless of the date/time fields), then you could (assuming you have an auto-incremental primary key in the datagps table (I assume it's called dataID for this example)):
ORDER BY datagps.dataID DESC
If these aren't showing the data you want - then there is something missing from your example (maybe data-types aren't DATETIME fields? - if not - then maybe a CONVERT to change them from their current type before ORDERing BY would be a good idea)
EDIT:
I've seen the screenshot and I'm confused as to what the issue is still. That appears to be showing everything in order. Are you implying that there are many more than 5 records? How many are you expecting?
Do you mean: for each record returned, you want the one row from the table datagps with the latest date and time attached to the result? If so, how about this:
# To show how the query will be executed
# comment to return actual results
EXPLAIN
SELECT
configgps.Fichagps, datacar.Ficha, groups.Nombre, datagps.Hora, datagps.Fecha,
datagps.Velocidad, datagps.Status, datagps.Calleune, datagps.Calletowo,
datagps.Temp, datagps.Longitud, datagps.Latitud, datagps.Evento,
datagps.Direccion, datagps.Provincia
FROM asigvehiculos
INNER JOIN datacar ON (asigvehiculos.Iddatacar = datacar.Id)
INNER JOIN configgps ON (datacar.Configgps = configgps.Id)
INNER JOIN clientdata ON (asigvehiculos.Idgroup = clientdata.group)
INNER JOIN groups ON (clientdata.group = groups.Id)
INNER JOIN datagps ON (configgps.Fichagps = datagps.Fichagps)
########### Add this section
LEFT JOIN datagps b ON (
configgps.Fichagps = b.Fichagps
# wrong condition
#AND datagps.Hora < b.Hora
#AND datagps.Fecha < b.Fecha)
# might prevent indexes to be used
AND (datagps.Fecha < b.Fecha OR (datagps.Fecha = b.Fecha AND datagps.Hora < b.Hora))
WHERE b.Fichagps IS NULL
###########
Group by configgps.Fichagps;
Similar question here only that that one uses outer joins.
Edit (again):
The conditions are wrong so corrected it. Can you show us the output of the above EXPLAIN query so we can pinpoint where the bottle neck is?
As hurikhan77 said, it will be better if you could convert both of the the columns into a single datetime field - though I'm guessing this would not be possible for your case (since your database is already being used?)
Though if you can convert it, the condition (on the join) would become:
AND datagps.FechaHora < b.FechaHora
After that, add an index for datagps.FechaHora and the query would be fast(er).
What you probably want is getting the maximum of (Fecha,Hora) per grouped dataset? This is a little complicated to accomplish with your column types. You should combine Fecha and Hora into one column of type DATETIME. Then it's easy to just SELECT MAX(FechaHora) ... GROUP BY Fichagps.
It could have helped if you posted your table structure to understand the problem.