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.
Related
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
A have a table called advert_property
And I have a table advert, which is not important, advert properties connects to advert by advert_id column in advert_property table.
I wrote this SQL request :
SELECT *
FROM `advert`
JOIN advert_property ON advert.id = advert_property.advert_id
WHERE (advert_property.property_id = 1
AND advert_property.property_value = "Манчего")
AND (advert_property.property_id = 2
AND advert_property.property_value = "козий")
What I want to get, is advert that have certain properties, for example : I want an idvert that have property_id = 1 and property_value = "Манчего" AND have property_id = 2 and property_value = "козий". SQL request returns null, how should I change my SQL request. Thanks!
Assuming I'm understanding your question correctly and you want to return all adverts that have both properties, then there are a couple ways of doing this using multiple joins, exists, in, group by...
Here is the method using multiple joins:
SELECT a.*
FROM `advert` a
JOIN advert_property ap ON a.id=ap.advert_id
AND ap.property_id = 1 AND ap.property_value = 'Манчего'
JOIN advert_property ap2 ON a.id=ap2.advert_id
AND ap2.property_id = 2 AND ap2.property_value = 'козий'
You can't return all columns * where the property_id is both 1 and 2 because a record can't have two values for the same field. You can, however, return records where the property_id is 1 OR 2. You could then have it only return DISTINCT advert_id where this is true:
SELECT DISTINCT advert_id
FROM `advert` JOIN advert_property ON advert.id=advert_property.advert_id
WHERE (advert_property.property_id = 1 AND advert_property.property_value = "Манчего")
OR (advert_property.property_id = 2 AND advert_property.property_value = "козий")
Query which you are trying to execute will never give you a result because it is trying to get a row with property id = 1 and property id = 2
For same row, there will never be two property_ids (1,2).
You need to review your where conditions.
My be what you need in where condition is as below(Either id will be 1 or id will be 2):
Try this :
(advert_property.property_id = 1 AND advert_property.property_value = "Манчего")
OR
(advert_property.property_id = 2 AND advert_property.property_value = "козий")
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
Im trying to make some sort of localization in my DB.
For example I have 3 tables(img 1). Languages table contains different languages. Localization table has 3 fields: "id" - id of the string, 'language' - language of the string(id and language are my primary key), 'value' - localized string. tableOne has 'id', 'Col1' and 'Col2' - these fields contain IDs of the localizeable strings.
So after localizing I expect to get one of green tables instead of original(depending on a language parameter).
I've made it this way and it works, but I'd like to know is there any other better way to make it because now I have to create INNER JOIN block for each column, which must be localized. Im just scared that it will be very very slow.
I tried to create a temporary table to select all records of the required language and then i was doing the same. Inner joins, but searches should be performed only among the records of one language. But its not working because i still had to use multiple inner joins with that temp table which is impossible.
SELECT
`One`.`id` AS 'id',
`loc1`.`value` AS 'Col1',
`loc2`.`value` AS 'Col2'
FROM
`tableOne` AS `One`
INNER JOIN
`localization` AS `loc1`
ON `loc1`.`id` = `One.Col1`
AND `loc1`.`language` = 'en'
INNER JOIN
`localization` AS `loc2`
ON `loc2`.`id` = `One.Col2`
AND `loc2`.`language` = 'en'
img 1
If you want to reduce the number of JOINS needed, try displaying the values in rows instead of columns. You could do so like this:
SET #lang := 'en';
SELECT 1, tmp.value
FROM(
SELECT value
FROM localization
WHERE language = #lang AND id IN(543, 345)) tmp;
I first set a language parameter, and then I pull all values for that language from the localization table, using the ids inside an IN operator. You'll get results like this:
| 1 | one |
| 1 | two |
If you have to use the format given in the first table, try doing one inner join where you pull for the specific language and ids like this:
SELECT t1.id, t1.col1, t1.col2,
CASE WHEN l.id = t1.col1 THEN l.value ELSE null END AS col1Value,
CASE WHEN l.id = t1.col2 THEN l.value ELSE null END AS col2Value
FROM firstTable t1
JOIN localization l ON l.id IN (t1.col1, t1.col2) AND l.language = #lang;
Unfortunately, this won't give you the final solution, it will give you values like:
| 1 | 543 | 345 | one | null |
| 1 | 543 | 345 | null | two |
To wrap those into one column and remove nulls, just add MAX():
This will run a case statement for each column you have, but it will only have one JOIN and looks a little more manageable:
SELECT t1.id,
MAX(CASE WHEN l.id = t1.col1 THEN l.value ELSE null END) AS col1Value,
MAX(CASE WHEN l.id = t1.col2 THEN l.value ELSE null END) AS col2Value
FROM firstTable t1
JOIN localization l ON l.id IN (t1.col1, t1.col2) AND l.language = #lang
GROUP BY t1.id;
Here is an SQL Fiddle example. I don't think the case blocks will bog you down too much, but let me know how this preforms against your actual database.
I have two tables reports & viewedids
the first one stores data and the second one stores user IDs that viewed data:
viewedids:
+-------+------+
| uid | rid |
+-------+------+
| 2 | 5 |
+-------+------+
each (uid,rid) means the uid has viewd rid
I want to select * from reports table and add view state (0 or 1) for current user to it. (A JOIN statement)
How can I do this?
You can use a LEFT JOIN:
SELECT reports.*, viewedids.uid IS NOT NULL as view_state
FROM
reports LEFT JOIN viewedids
ON reports.id = viewedids.rid
AND viewedids.uid = #current_user
This will return all reports, and will try to join reports table with viewedids ON reports.id = viewedids.rid AND viewedids.uid = #current_user. If the join succedes, viewedids.uid will be not null, and viewedids.uid IS NOT NULL will be evaluated to 1. It will be evaluated 0 otherwise.
Please see fiddle here.