MySQL adapt JOIN according to the scenario - mysql

I have two tables with the following structures:
exp_hotel
hot_id | hot_webid | hot_name | hot_starrating | hot_brandbool | hot_latitude | hot_longitude
exp_result
res_id | res_idHotel | res_rank | res_price | res_userRating | res_oldPrice | res_posa | res_date | res_date | res_que_id
res_posa has sometimes either the value 'xxx' or the value 'yyy'.
If it has the value 'xxx', the SELECT should be something like:
SELECT * FROM exp_hotel JOIN exp_result ON hot_id = res_idHotel
And if res_posa value is 'yyy' than the SELECT would be:
SELECT * FROM exp_hotel JOIN exp_result ON hot_webid = res_idHotel
Is there a way how to create a query wich would select everything with the correct JOIN structure directly from MySQL without going through some PHP arrays and so on?

You can use conditional join like below.
SELECT * FROM exp_hotel JOIN exp_result ON
(res_posa = 'XXX' and hot_id = res_idHotel)
OR
(res_posa = 'YYY' and hot_webid = res_idHotel);
Mock up fiddle here.

Related

How to use SELECT CASE with subquery

I have a problem with my SELECT query. I try to retrieve a tagValue name "CallsBy", but only if the value is "Entry point" - otherwise I want to compute the calls by another sub query.
I have 3 tables :
t_object: with all the classes
t_operation: with all the operations, link with t_object.Object_ID = t_operation.Object_ID
t_operationtag: with all the tag value for each operation, link with t_operation.OperationID = t_operationtag.ElementID
Here is a demo with my tables I have:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=b6a1672948db816aefb23b8dc8d8e01d
The result for the class 1 would be :
Name Operation | CallsBy
-----------------+------------------------------------
class1Operation1 | class2Operation2, class3Operation1
class1Operation2 | class2Operation3
class1Operation3 | class2Operation3
The result for the class 2 would be :
Name Operation | CallsBy
-----------------+----------------------------------------------------
class2Operation1 | class1Operation1, class3Operation1,class3Operation3
class2Operation2 | Entry point of Tabidi
class2Operation3 | class1Operation1
The result for the class 3 would be :
Name Operation | CallsBy
-----------------+---------------------------------------
class3Operation1 | class1Operation2, class3Operation1
class3Operation2 | Entry point of Tabada
class3Operation3 | class2Operation1
Here is the sub query to compute the CallsBy if is not a "Entry point" :
select
t2.name, group_concat(t1.name)
from
t_operation t1
left join
(select to3.ElementID, to2.name
from t_object to1
left join t_operation to2 on to1.Object_ID = to2.Object_ID
left join t_operationtag to3 on find_in_set(to2.Name, to3.VALUE)
where to3.Property = 'Calls'
and to1.Object_ID = '1') t2 on t1.OperationID = t2.ElementID
where
t2.ElementID is not null
group by
t2.name;
I try to write the query but I didn't get any result...
First off let me say that I believe your database table structure and relationships might not be optimal, it seems complex for just 3 tables.
Also, I believe there is a difference between the data you listed in this post and the data that you listed in the db fiddle example. I created all of the tables and populated them with the data you gave and the results of the query I came up with don't exactly match the data you listed above as the expected result.
However, I believe this query will get you what you want: if the corresponding t_operationtag.Property = 'CallsBy' and the t_operationtag.VALUE contains Entry point then it returns the t_operationtag.VALUE else it group concats the t_operation.Name values for the given object id:
SELECT top.Name,
CASE WHEN topt2.VALUE IS NOT NULL THEN topt2.Value
ELSE GROUP_CONCAT(topt1.VALUE)
END AS CallsBy
FROM t_operation AS top
JOIN t_operationtag AS topt1 ON topt1.ElementID = top.OperationID
LEFT JOIN t_operationtag AS topt2 ON topt2.ElementID = top.OperationID
AND topt2.Property = 'CallsBy'
AND topt2.VALUE LIKE '%Entry point%'
JOIN t_object AS obj ON obj.Object_ID = top.Object_ID
WHERE obj.Object_ID = 1 -- or 2 or 3
GROUP BY top.Name, topt1.PropertyID, topt2.PropertyID
ORDER BY top.Name
I hope this helps.

Static SQL query replace to dynamic column

I have following query:
http://www.sqlfiddle.com/#!9/752e34/3
This query use SELECT in SELECT queries.
"SELECT a.*
,(SELECT s.value FROM tbl_scd AS s WHERE s.tag_id = 1 AND s.main_id = a.id ORDER BY s.date_time DESC LIMIT 1) AS title
,(SELECT s.value FROM tbl_scd AS s WHERE s.tag_id = 2 AND s.main_id = a.id ORDER BY s.date_time DESC LIMIT 1) AS alt
FROM tbl_main AS a
WHERE 1;"
Now I'm looking for a solution to add a new row into tbl_tag without change the above query (that the SELECT in SELECT part will be dynamic) to get a reference to tbl_tag
To get this:
+----+---------------+-----------+-----------+--------------+
| id | date | title | alt | new_column |
+----+---------------+-----------+-----------+--------------+
| 1 | 2018-10-10 | test1-1 | test1-3 | NULL |
| 2 | 2018-10-11 | test2-1 | test2-1 | NULL |
+----+---------------+-----------+-----------+--------------+
It would be great to get an idea or help.
Thanks
Your last comment on your question about using JOIN makes it clearer to me (I think) what you are after. JOINs will definitely help you a lot here, in place of the rather cumbersome query you are currently using.
Try this:
SELECT
tbl_main.date,
tblA.value AS title,
tblB.value AS alt
FROM
tbl_main
INNER JOIN (SELECT main_id, tag_id, value
FROM tbl_scd
INNER JOIN tbl_tag ON (tbl_scd.tag_id = tbl_tag.id)
WHERE tbl_tag.name = 'title') tblA
ON (tbl_main.id = tblA.main_id)
INNER JOIN (SELECT main_id, tag_id, value
FROM tbl_scd
INNER JOIN tbl_tag ON (tbl_scd.tag_id = tbl_tag.id)
WHERE tbl_tag.name = 'alt') tblB
ON (tbl_main.id = tblB.main_id);
I think this will get you much closer to a general solution to what it looks like you are trying to achieve, or at least point you in a good direction with using JOINs.
I also think you might benefit from re-thinking your database design, because this kind of pivoting rows from one table into columns in a query output can be an indicator that the data might be better off structured differently.
In any case, I hope this helps.

MySQL column flattened to string

I am trying to avoid passing two separate MySQL (version 5.6.37) queries, and using transactions. I think this can be done in a single query, but I need to know where I'm going wrong.
If I use this query:
SELECT titles.t_id,title,cover,pageData.pageNum
FROM titles
JOIN biblio ON titles.t_id = biblio
JOIN pageData ON biblio.t_id = pageData.t_id
WHERE titles.t_id = '1';
It successfully returns a result with three columns of redundant data, and only one column of new data (p_id):
t_id | title | cover | pageNum
1 | The Art of the Deal | 32.jpg | 1
1 | The Art of the Deal | 32.jpg | 2
1 | The Art of the Deal | 32.jpg | 3
1 | The Art of the Deal | 32.jpg | 4
1 | The Art of the Deal | 32.jpg | 5
I think there is a way to modify the query so that the new data in the pageNum column is flattened into a single result (i.e. converted from integer values to a delimited string), like this:
t_id | title | cover | p_id
1 | The Art of the Deal | 32.jpg | 1,2,3,4,5
I have been experimenting with a sub-SELECT within the SELECT, but I have consistent syntax errors. Is there a way to combine these two queries below to get the above result?
SELECT titles.t_id,title,cover
FROM titles
JOIN biblio ON titles.t_id = biblio
WHERE titles.t_id = '1';
and
SELECT pageData.pageNum FROM pageData WHERE pageData.t_id = '1'
You can use GROUP_CONCAT in combination with GROUP BY for that.
SET SESSION group_concat_max_len = ##max_allowed_packet
SELECT
titles.t_id
, title,cover
, GROUP_CONCAT(pageData.pageNum) AS p_id
FROM titles
JOIN biblio ON titles.t_id = biblio
JOIN pageData ON biblio.t_id = pageData.t_id
WHERE titles.t_id = '1'
GROUP BY
t_id
, title
, cover
Use the GROUP_CONCAT function. Also assuming you meant JOIN biblio ON titles.t_id = biblio.t_id
SELECT t.t_id, title, cover, GROUP_CONCAT(pageData.pageNum) AS pageNum
FROM titles t
JOIN biblio b ON t.t_id = b.t_id
JOIN pageData p ON b.t_id = p.t_id
WHERE t.t_id = '1'
GROUP BY t.t_id, title, cover
The result you need can be easily accomplished using the MySQL function GROUP_CONCAT().
In order to produce a valid SQL query and get the results you expect, you also need to add a GROUP BY clause to the query and put in it all the other columns that appear in the SELECT clause:
SELECT titles.t_id, title, cover, GROUP_CONCAT(pageData.pageNum) AS p_id
FROM titles
JOIN biblio ON titles.t_id = biblio
JOIN pageData ON biblio.t_id = pageData.t_id
WHERE titles.t_id = '1'
GROUP BY titles.t_id, title, cover

MySQL left join with default values

I have a couple of tables, one with source data which I'll call SourceData and another which defines overridden values for a given user if they exist called OverriddenSourceData.
The basic table format looks something this like:
SourceData
| source_id | payload |
--------------------------------
| 1 | 'some json' |
| 2 | 'some more json' |
--------------------------------
OverriddenSourceData
| id | source_id | user_id | overrides
| 1 | 2 | 4 | 'a change' |
------------------------------------------
For a given user, I'd like to return all the Source data rows with the overrides column included. If the user has overridden the source then the column is populated, else it is null.
I started by executing a left join and then including a condition for checking the user like so:
SELECT A.source_id, A.payload, B.overrides from SourceData A
LEFT JOIN OverriddenSourceData B
ON A.source_id = B.source_id
WHERE user_id = 4
but then source rows that weren't overridden wouldn't be included ( it was acting like an inner join) (e.g source id 1)
I then relaxed the query and used a strict left join on source_id.
SELECT A.source_id, A.payload, B.overrides from SourceData A
LEFT JOIN OverriddenSourceData B
ON A.source_id = B.source_id
# WHERE user_id = 4
This can return more data than I need though (e.g other users who have overridden the same source data) and then I have to filter programatically.
It seems like I should be able to craft a query that does this all the DB level and gives me what I need. Any help?
You should add your condition on LEFT JOIN clause, if you use WHERE, mysql will do it with INNER JOIN, so try this;)
SELECT A.source_id, A.payload, B.overrides from SourceData A
LEFT JOIN OverriddenSourceData B
ON A.source_id = B.source_id
AND B.user_id = 4

sql query seems to return no result

Certain queries that work fine in the mysql command line client don't seem to return
anything at all (no error, no result) in phpMyAdmin.
I get the impression it's related to nested queries.
Here's an example:
select * from
(select
(select min(data) from census2 where
census2.monkey=samplecollection.monkeyid and
date(collectiontime)=date(census2.timestamp)) census
from samplecollection,biograph,hormone,plate
where plate.hormone='Testosterone' and hormone.plateid=plate.plateid and
not specialcontentcode like '%UR%' and thermos!='F' and
biograph.id=monkeyid and samplecollection.sampleid=hormone.sampleid)
t1 limit 3;
+--------+
| census |
+--------+
| GFF |
| GRF |
| GRF |
+--------+
3 rows in set (5.09 sec)
If I extract the inner query (and put the limit on it) then I get a result.
The structure of your query if too complex and clearly not optimized and it may cause the problem you've releved.
Here is the same query with a bit of refactoring:
SELECT *
FROM samplecollection SC
INNER JOIN (SELECT C2.monkeyid
,MIN(C2.data) AS [census]
FROM census2 C2
INNER JOIN samplecollection SC2 ON SC2.monkeyid = C2.monkey
AND DATE(SC2.collectiontime) = DATE(C2.timestamp)
AND SC2.thermos != 'F'
AND SC2.specialcontentcode NOT LIKE '%UR%'
GROUP BY C2.monkeyid) T ON T.monkeyid = SC.monkeyid
INNER JOIN biograph B ON B.id = SC.monkeyid
INNER JOIN hormone H ON H.sampleid = SC.sampleid
INNER JOIN plate P ON P.plateid = H.plateid
AND P.hormone = 'Testosterone'
LIMIT 3
The answer comes late but it may be useful for some people to see how some very complex query structures can be simplified when using JOIN clauses.
Hope this will help.