MySQL Multiple select - mysql

I have an indextable which hold a content_id and type colomn.
(eg. content_id=1, type=audio. content_id=1, type=video) Would it be possible to get the right data based upon content_id from the right tables (tables: contentvideo, contentaudio)?

I take it you want to retrieve the content via a single select statement? If the layout of your content tables is exactly the same you can use UNION:
SELECT ca.* FROM contentaudio ca, indextable it
WHERE ca.content_id = it.content_id
AND it.content_type = 'audio'
UNION
SELECT cv.* FROM contentvideo cv, indextable it
WHERE cv.content_id = it.content_id
AND it.content_type = 'video'
ORDER BY content_id /* or whatever */
Note that if content_id is not unique across all of your content tables, you may get multiple records (of different types) for the same content_id. Also note that if you're selecting a particular record based on some indextable key, you'll need to add it to each part of the union.
If your content tables have different layouts, you can use an OUTER JOIN instead and retrieve your various content types as part of one record (missing ones will be returned as NULLs):
SELECT * FROM indextable it
LEFT OUTER JOIN contentaudio ca ON it.content_id = ca.content_id
LEFT OUTER JOIN contentvideo cv ON it.content_id = cv.content_id
WHERE it.some_key = ?
Note that content_type is not involved in this case at all (nor do you really need it for UNION select either).

Have you tried CASE, or IF?
http://dev.mysql.com/doc/refman/6.0/en/case-statement.html

Related

SQL Left Join on more then 5 tables

The code is retreating what I want it to retrieve, though it does 4 times more that I need.
I have a table called property with a PK property_ID. A property has a title, short description, long description and it belongs to a point of interest, which in turn is connected to a city. Title, short&long description have all independent link tables, in which a link is created between a property and the corresponding translation in the translation table.
I am using left joins to collect all the desired information, below in the image attachment you can see the results.
The desired result would be that it would return only 2 rows, with the corresponding translation of the title, short and long description. At the moment is returning 8 rows.
The issue that I noticed is it orders the columns in a weird way.
If you look closer, you can notice that title and titleLangCode are ordered correctly after titleLangCode, while long and short descriptions are ordered by their own langCode. Grouping them wont work since they are ordered differently, so ordering them has no effect, union didnĀ“t work either because of the column numbers, even tried distinct but with no avail.
The code:
select
property.*,
title_translation.title,
title_translation.langCode as titleLangCode,
short_desc_translation.shortDescription,
long_desc_translation.longDescription,
short_desc_translation.langCode as shortLangCode,
long_desc_translation.langCode as longLangCode,
property_city_poi.city_poi_link_ID
from
property
left join
title_link
on
property.property_ID = title_link.property_ID
left join
title_translation
on
title_link.title_link_ID = title_translation.title_link_ID
left join
short_desc_link
on
property.property_ID = short_desc_link.property_ID
left join
short_desc_translation
on
short_desc_link.short_desc_link_ID = short_desc_translation.short_desc_link_ID
left join
long_desc_link
on
property.property_ID = long_desc_link.property_ID
left join
long_desc_translation
on
long_desc_link.long_desc_link_ID = long_desc_translation.long_desc_link_ID
left join
property_city_poi
on
property.property_ID = property_city_poi.property_ID
where
property.property_ID = 10
Is there a possibility of somehow combing limit with group ? I tried but have not succeed.
title_link
title_link_ID
property_ID
dateCreated
title_translation
title_translation_ID
title_link_ID
langCode
title
short_desc_link
short_desc_link_ID
property_ID
dateCreated
short_desc_translation
short_desc_translation_ID
short_desc_link_ID
langCode
shortDescription
long_desc_link
long_desc_link_ID
property_ID
dateCreated
long_desc_translation
long_desc_translation_ID
long_desc_link_ID
langCode
shortDescription
If I understand this correctly, the issue is that you have multiple translation tables which have a 1 to many relationship to the property. So after the first join you have 2 rows then 4 and then 8 with all combinations of languages.
You can limit this by joining on 2 conditions.
... property p
INNER JOIN titel_link tl on p.id = tl.propertyid
INNER JOIN short_desk_link sdl on p.id = tl.propertyid AND tl.langCode = sdl.langCode
I've simplified this a little and used aliases for table names to shorten the join conditions.
Edit: I'd say this is also a sign of bad database design. you should probably introduce a table 'language' and then a mapping table property-translation mapping. Not sure if this is under your control but a setup with tables like this would be better.
Property: all the details for the property.
Language: A listing of all the languages with Id.
FieldId: A list of named columns that you have in your database.
Translation: A combination of Property, Language and Field on which you can then have a single translation for that field.

Replace the id's with name using single query

SELECT team_with.participant1,team_with.participant2,team_with.participant3
FROM event,team_with
WHERE team_with.for_event_no=event.event_no AND
event.event_no=4 AND
team_with.participant1=9 OR
team_with.participant2=9 OR
team_with.participant3=9;
I have written the particular query, and obtained the required id's in a row. I am not able to modify this query such that, in place of these id's, names connected to the id's are displayed.
The student_detatil table consists of PK(sam_id) and the attribute name.
IDs displayed by the present query are FKs connected to student_detail.sam_id..
It seems like a bad design to multiply columns storing different participants. Consider creating a separate row for each participant and storing them in a table. Your joining logic would also be easier.
Also, please use explicit JOIN syntax - it makes the query clearer and easier to understand by separating join logic with conditions for data retrieval.
Remember that operator AND has a precedence over OR, so that your event.event_no = 4 does not apply to each participant condition. I believe this was a mistake, but you are the one to judge.
As to the query itself, you could apply OR conditions into join, or simply join the student_detail table thrice.
SELECT
s1.name,
s2.name,
s3.name
FROM
event e
INNER JOIN team_with t ON t.for_event_no = e.event_no
LEFT JOIN student_detail s1 ON s1.sam_id = t.participant1
LEFT JOIN student_detail s2 ON s2.sam_id = t.participant2
LEFT JOIN student_detail s3 ON s3.sam_id = t.participant3
WHERE
e.event_no = 4
AND ( t.participant1=9 OR t.participant2=9 OR t.participant3=9 );

Select from 3 tables if result is empty

I'm using 3 tables to collect data from. The proces looks like:
User write VIN to form
Script search in table 1 for case_id and country base on that vin
number
After that he use case_id and country for search in table number 2
and get calculation id from there
Base on that calculation id and case id it search in 3th table
.
My script looks like this:
SELECT
cases.case_id,
cases.lastcalc_model_options,
cases.country,
calculations.calculation_id,
calculations.license,
positions.text
FROM cases
INNER JOIN calculations ON(cases.case_id =calculations.case_id
AND cases.country = calculations.country)
INNER JOIN positions ON(calculations.case_id = positions.case_id
AND calculations.calculation_id = positions.calculation_id)
WHERE vin ='ABCDEFGH'
This select work correctly, problem start when for example there is for example no result in table positions with that case_id and calculation_id. Instead of give back atleast everything it found in other tables it return NOTHING.
Is there a way to change this kind of komplex SELECT to return everything it found not return something only when every condition is TRUE?
Your problem is the INNER JOIN. Using INNER JOIN your result only contains entries present in all tables. Try using LEFT JOIN instead.
SELECT
cases.case_id,
cases.lastcalc_model_options,
cases.country,
calculations.calculation_id,
calculations.license,
positions.text
FROM cases
LEFT JOIN calculations ON(cases.case_id =calculations.case_id
AND cases.country = calculations.country)
LEFT JOIN positions ON(calculations.case_id = positions.case_id
AND calculations.calculation_id = positions.calculation_id)
WHERE vin ='ABCDEFGH'
See this stackoverlow answer for some more indepth information.
INNER JOIN returns rows from both tables only if there is a match between the columns in both tables.
You may try LEFT JOIN or FULL OUTER JOIN instead.

Populate 'temporary' columns with corresponding values during MySQL join query, also limit

I'm doing several MySQL joins to get template variables (i.e. custom fields) and their values (in MODX Evo but it's irrelevant - this is a general MySQL query).
I'm looking ideally to be able to create 2 temporary columns in order to use SORT BY in the query, or something to this effect. I'd like to populate the values for 'event_date' and 'event_featured' for their corresponding id's in these new columns - then I could then sort the results by these columns.
On a very related note I would like to limit the results to 20 for each unique id, not for each row as would happen if I added LIMIT- it would crop the below result to the . Can this be accomplished at the same time?
Anybody know how / if these are possible? Many thanks in advance.
Code and image of the results below:
SELECT DISTINCT
content.id, content.pagetitle, content.template , content.published,
templates.templatename,
tv_props.name,
tv_values.value
FROM `modx_site_content` AS `content`
LEFT JOIN `modx_site_templates` AS `templates` ON content.template=templates.id
LEFT JOIN `modx_site_tmplvar_templates` AS `template_tvs` ON templates.id=template_tvs.templateid
LEFT JOIN `modx_site_tmplvars` AS `tv_props` ON template_tvs.tmplvarid=tv_props.id
LEFT JOIN `modx_site_tmplvar_contentvalues` AS `tv_values` ON template_tvs.tmplvarid=tv_values.tmplvarid
WHERE templates.id=89
AND (
tv_props.name='event_featured'
OR tv_props.name='event_link_through'
OR tv_props.name='event_title'
OR tv_props.name='event_date'
OR tv_props.name='event_date_text'
OR tv_props.name='event_short_description'
OR tv_props.name='event_list_image'
);
Link to full-size image
You're going to need a couple of virtual tables, also known as subqueries, to retrieve these two properties of events from your name/value table. The generic name for this kind of query is a "pivot," for your information.
The mental knack is to think of the subquery as a virtual table which you can use in a surrounding query. The subquery for event_date looks like this, I believe.
SELECT content.id AS id,
tv_values.value AS event_date
FROM modx_site_content AS content
LEFT JOIN modx_site_templates AS templates
ON content.template=templates.id
LEFT JOIN modx_site_tmplvar_templates AS template_tvs
ON templates.id=template_tvs.templateid
LEFT JOIN modx_site_tmplvars AS tv_props
ON template_tvs.tmplvarid=tv_props.id
LEFT JOIN modx_site_tmplvar_contentvalues AS tv_values
ON template_tvs.tmplvarid=tv_values.tmplvarid
WHERE tv_props.name = 'event_date'
This little query produces a resultset that's a table relating content id to event date. I honestly don't understand your schema well enough to know if there's just one event date for each content id, so you might need to adjust this query to SELECT more columns. As you debug this, you should try out the subquery and make sure it's giving the results you hope for.
Then, when you're sure the subquery is OK, you join that subquery into your overall query, generically like so.
SELECT DISTINCT
content.id, event_date.event_date, templates.column,
table.column, table.colum, etc, etc
FROM modx_site_content AS content
LEFT JOIN table ON condition
LEFT JOIN (
SELECT content.id AS id,
tv_values.value AS event_date
FROM modx_site_content AS content
LEFT JOIN modx_site_templates AS templates
ON content.template=templates.id
LEFT JOIN modx_site_tmplvar_templates AS template_tvs
ON templates.id=template_tvs.templateid
LEFT JOIN modx_site_tmplvars AS tv_props
ON template_tvs.tmplvarid=tv_props.id
LEFT JOIN modx_site_tmplvar_contentvalues AS tv_values
ON template_tvs.tmplvarid=tv_values.tmplvarid
WHERE tv_props.name = 'event_date'
) AS event_date ON event_date.id = content.id
LEFT JOIN etc, etc, etc.
WHERE etc etc etc
Do you see how that goes? You can use tablename AS table or (some query) AS table interchangeably. You can also define a VIEW in your schema that provides the same data, and name it in your query. That's a handy way to make your queries less hairy.
By the way, you'll boost performance if you change
AND (
tv_props.name='event_featured'
OR tv_props.name='event_link_through'
OR tv_props.name='event_title' etc )
to
AND tv.props.name IN ('event_featured',
'event_link_through',
'event_title', etc)
You've probably noticed I'm a bit of a stickler for indentation in SQL queries. I find this helpful; I often find mistakes while I'm fixing up the indentation. Your practice may vary.

MySQL LEFT JOIN with optional value in second table

I have two tables: projects and user_licenses.
I'd like to grab the entire list of projects from the database, as well as the user's current license state. The licenses table has a user ID field which I check against a $_SESSION variable for the value. The thing is, they might not have a license, or a non-logged in visitor may want to see the projects list. My question is this: How can I get the data from the left table always display, but only grab data for that row from the right table when certain conditions are met?
The query I have at the moment is this:
SELECT Projects.*,
UserLicenses.*
FROM Projects
LEFT JOIN UserLicenses ON Projects.id = UserLicenses.project_id
WHERE UserLicenses.user_id = 12
ORDER BY name ASC
Add any extra conditions to the on clause of the left join. They will only affect the joined table.
SELECT Projects.*,
UserLicenses.*
FROM Projects
LEFT JOIN UserLicenses
ON Projects.id = UserLicenses.project_id
and UserLicenses.user_id = 12
and UserLicences.Question = '6*7'
and UserLicences.Answer = 42
ORDER BY name ASC
This will return projects without matching licenses.
Move the UserLicenses condition away from WHERE, and up to the JOIN condition. By having it in the WHERE part, you will never see those "left" rows because they are filtered away.
You can also probably use WHERE (UserLicenses.user_id = 12 OR UserLicenses.user_id IS NULL)
Don't do that. Just move it to the join condition like this:
LEFT JOIN UserLicenses ON
(Projects.id = UserLicenses.project_id AND UserLicenses.user_id = 12)
You can use LEFT JOIN
If its conditions match then values show otherwise null value shows.