I am using multi language supported database. But i don't know how to query translated text from database based on Language.
Here is my DDL for tables. Please ask me if you have any question.
Category Table
CREATE TABLE IF NOT EXISTS `catalog_category` (
`id` int(10) unsigned NOT NULL,
`name_translation_id` int(10) unsigned NOT NULL,
`description_translation_id` int(10) unsigned NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Language Table
CREATE TABLE IF NOT EXISTS `languages` (
`id` int(10) unsigned NOT NULL,
`language_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`language_code` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Language Translation table
CREATE TABLE IF NOT EXISTS `language_translation` (
`id` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Language Translation Entry Table
CREATE TABLE IF NOT EXISTS `language_translation_entry` (
`id` int(10) unsigned NOT NULL,
`translation_id` int(10) unsigned NOT NULL,
`language_id` int(10) unsigned NOT NULL,
`translation_text` text COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Currently i am using this query to get results but it is giving me from only name. I want description also in results.
SELECT lte.translation_text as name FROM catalog_category AS cc
LEFT JOIN language_translation lt ON lt.id = cc.name_translation_id
LEFT JOIN language_translation_entry AS lte ON lte.translation_id = lt.id
LEFT JOIN languages AS l on l.id = lte.language_id
WHERE l.language_code = "en"
I need to get translated text from Translation Entry Table for category name and description based on language code in a single query.
You can use subselects:
SELECT
(select lte.translation_text
from language_translation_entry AS lte
JOIN languages AS l on l.id = lte.language_id
WHERE lte.translation_id = cc.name_translation_id
and l.language_code = "en"
) as name,
(select lte.translation_text
from language_translation_entry AS lte
JOIN languages AS l on l.id = lte.language_id
WHERE lte.translation_id = cc.description_translation_id
and l.language_code = "en"
) as description
from catalog_category AS cc
This assumes you have max 1 entry for each (translation_id, laguage_id), otherwise this will give you an error, because these subselects can only return one value.
I left out the table language_translation, because you probably only use it to get your autoincrement values, and is not required for the join.
An alternative would be to use another join for each column you need translated, you can e.g. use
SELECT lte.translation_text as name,
lt2.translation_text as description,
FROM catalog_category AS cc
LEFT JOIN language_translation_entry AS lte
ON lte.translation_id = cc.name_translation_id
LEFT JOIN languages AS l on l.id = lte.language_id
LEFT JOIN language_translation_entry AS lte2
ON lte2.translation_id = cc.name_translation_id
and lte2.language_id = l.id
WHERE l.language_code = "en"
where I assume that you want both translation to be the same language.
Btw., left join actually works here like a normal join, since you use where l.language_code = "en", it has to exists. If you want a real left join, you have to move that condition to the on-clause, so it can be missing (if there is a possibility that your translations are missing and you still want to get a result from your query). For description, it already is allowed to be missing to make the code shorter, otherwise you would have to move the condition from on to the where clause, so change the last part to
LEFT JOIN language_translation_entry AS lte2
ON lte2.translation_id = cc.name_translation_id
WHERE l.language_code = "en" and lte2.language_id = l.language_id
Related
I want to join multiple tables using JOINs, and mysql/mariadb is refusing to find one column. The column exists, of course, and I can't figure out what the cause might be.
Table Layout
CREATE TABLE `shops` (
`id` int(11) NOT NULL,
`name` varchar(32) NOT NULL,
);
CREATE TABLE `shops2categories` (
`shopid` int(11) NOT NULL,
`categoryid` int(11) NOT NULL,
);
CREATE TABLE `categories` (
`id` int(11) NOT NULL,
`name` varchar(64) NOT NULL,
);
CREATE TABLE `categories2items` (
`itemid` int(11) NOT NULL,
`categoryid` int(11) NOT NULL
);
CREATE TABLE `items` (
`id` int(11) NOT NULL,
`name` varchar(32) NOT NULL,
);
Query:
In order to avoid confusion with aliases, I now ran the query with the original table names.
SELECT
shops.name,
categories.name,
items.name
FROM shops
LEFT JOIN shops2categories ON shops2categories.shopid = shops.id
LEFT JOIN categories2items ON categories2items.categoryid = categories.id
LEFT JOIN categories ON categories.id = shops2categories.categoryid
LEFT JOIN items ON items.id = categories2items.itemid
Error Message:
#1054 - Unknown column 'categories.id' in 'on clause'
No matter how I restructured my query (foreign key first, primary key first, items table first, categories table first, ..., using different JOIN types), I can't seem to get this to work. Also I read through a whole lot of SO questions to this topic, but I feel that the order is correct, I am not inserting nor updating, and it's not about quoting. Something is fundamentally broken in my concept, and I'm very keen on learning what this could be.
Look at your from clause. You reference c.id before you have defined c.
A table cannot be referenced until it is defined in the FROM clause.
You would seem to want:
FROM shops s LEFT JOIN
shops2categories s2c
ON s2c.shopid = s.id LEFT JOIN
categories c
ON c.id = s2c.categoryid LEFT JOIN
categories2items c2i
ON c2i.categoryid = c.id LEFT JOIN
items i
ON i.id = c2i.itemid
I have a weird schema but when it was designed it seemed like a good idea at the time. I have one master table, lesson_objects, that has foreign keys linking to the vocabulary, video and quizzes tables.
vocab table:
CREATE TABLE `se_vocab` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`vocab_word` VARCHAR(255) NOT NULL,
`vocab_audio` INT(10) NULL DEFAULT '0',
`vocab_image` INT(10) NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
video table:
CREATE TABLE `se_video` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`video_name` VARCHAR(255) NOT NULL,
`video_description` MEDIUMTEXT NOT NULL,
`video_file_name` VARCHAR(50) NULL DEFAULT NULL,
`video_url` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
)
quizzes table:
CREATE TABLE `se_quizzes` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`quiz_name` VARCHAR(80) NOT NULL,
`quiz_description` TINYTEXT NULL,
PRIMARY KEY (`id`)
)
lesson objects (contains foreign keys of previous tables)
CREATE TABLE `se_lesson_org` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`lesson_id` INT(10) NOT NULL,
`section_object_type` ENUM('video','vocabulary','quiz') NOT NULL,
`section_object_id` INT(10) NOT NULL,
PRIMARY KEY (`id`)
)
I'm trying to create a query that returns all the records from lesson_objects but also includes the data in the columns for the type in that record (vocabulary, etc.)
For example:
Only my query returns no rows, while ideally it should turn multiple rows with every record containing SOME empty columns. E.g. if it isn't vocabulary, the columns for quiz and video will be empty.
My attempts are very bad, but here is one for the sake of guidance:
SELECT
lo.id, lo.section_object_type, lo.section_object_id,
vo.id, vo.vocab_text, vo.vocab_image, vo.vocab_audio
vi.id, vi.video_name, vi.video_url,
q.id, q.quiz_name
FROM se_lesson_org lo, se_vocab vo, se_video vi, se_quizzes q
WHERE lo.section_object_id = vo.id
OR lo.section_object_id = vi.id
OR lo.section_object_id = q.id
Any help / comments would be appreciated. Thanks.
Use LEFT JOIN to return all rows from se_lesson_org. Additionally, add a JOIN condition to match for the specific section_object_type
SELECT
lo.*,
vo.*,
vi.*,
q.*
FROM se_lesson_org lo
LEFT JOIN se_vocab vo
ON vo.id = lo.section_object_id
AND lo.section_object_type = 'vocabulary'
LEFT JOIN se_video vi
ON vi.id = lo.section_object_id
AND lo.section_object_type = 'video'
LEFT JOIN se_quizzes q
ON q.id = lo.section_object_id
AND lo.section_object_type = 'quiz'
Note: Avoid using old-style JOIN syntax. Read this article by Aaron Bertrand.
Sounds like you are looking for a LEFT JOIN on each of these tables. That way if the foreign key is valid you will show values and if it isn't valid you will just get NULL for the related columns.
SELECT
lo.id, lo.section_object_type, lo.section_object_id,
vo.id, vo.vocab_text, vo.vocab_image, vo.vocab_audio
vi.id, vi.video_name, vi.video_url,
q.id, q.quiz_name
FROM se_lesson_org lo
LEFT JOIN se_vocab vo ON vo.id = lo.section_object_id
AND lo.section_object_type = 'vocabulary'
LEFT JOIN se_video vi ON vi.id = lo.section_object_id
AND lo.section_object_type = 'video'
LEFT JOIN se_quizzes q ON q.id = lo.section_object_id
AND lo.section_object_type = 'quiz'
Notice also how this syntax makes it clear how each table is being connected into the rest of the query rather than having a whole mess of conditions in the WHERE clause at the end.
The Queries are working perfectly each one separatedly:
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN atp_surfaces asf ON m2s.surfaces_id = asf.surfaces_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id;
SELECT att.tournament_type_name, am.*
FROM atp_matchs_to_tournament_type m2s
LEFT JOIN atp_tournament_type att ON m2s.tournament_type_id = att.tournament_type_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id;
The tables 'atp_matchs_to_surfaces' and 'atp_matchs_to_tournament_type' are defined in that way:
CREATE TABLE IF NOT EXISTS `atp_matchs_to_tournament_type` (
`tournament_type_id` int(4) NOT NULL,
`matchs_id` int(6) NOT NULL,
PRIMARY KEY (`tournament_type_id`,`matchs_id`)
CREATE TABLE IF NOT EXISTS `atp_matchs_to_surfaces` (
`surfaces_id` int(4) NOT NULL,
`matchs_id` int(6) NOT NULL,
PRIMARY KEY (`surfaces_id`,`matchs_id`)
And the other Tables with all the data:
CREATE TABLE IF NOT EXISTS `atp_matchs` (
`matchs_id` int(7) NOT NULL AUTO_INCREMENT,
`tournament_name` varchar(36) NOT NULL,
`tournament_year` year NOT NULL,-- DEFAULT '0000',
`tournament_country` varchar(26) NOT NULL,
`match_datetime` datetime NOT NULL,-- DEFAULT '0000-00-00 00:00:00',
`match_link` varchar(85) NOT NULL,
`prize_money` int(12) NOT NULL,
`round` varchar(8) NOT NULL,-- DEFAULT '1R',
`sets` varchar(34) NOT NULL,-- DEFAULT '0-0',
`result` varchar(4) NOT NULL,-- DEFAULT '0-0',
`p1_odd` decimal(4,2) NOT NULL,-- DEFAULT '0.00',
`p2_odd` decimal(4,2) NOT NULL,-- DEFAULT '0.00',
PRIMARY KEY (`matchs_id`)
CREATE TABLE IF NOT EXISTS `atp_surfaces` (
`surfaces_id` int(4) NOT NULL AUTO_INCREMENT,
`surface_name` varchar(24) NOT NULL,
PRIMARY KEY (`surfaces_id`)
CREATE TABLE IF NOT EXISTS `atp_tournament_type` (
`tournament_type_id` int(4) NOT NULL AUTO_INCREMENT,
`tournament_type_name` varchar(22) NOT NULL,
PRIMARY KEY (`tournament_type_id`)
I want in the same Query all the records of match and surface name+tournament type. It's clear? I hope...
I tried to implement this with SubQueries: http://www.w3resource.com/mysql/subqueries/ and How can an SQL query return data from multiple tables but i can't do it to work.
OK, this is your current schema. As you can see, one match can be played on multiple surfaces and one match can be played within multiple tournament types.
If this schema is OK, you can get your result with this query:
SELECT am.*, asu.surface_name, att.tournament_type_name
FROM atp_matchs AS am
LEFT JOIN atp_matchs_to_surfaces AS m2s ON m2s.matchs_id = am.matchs_id
LEFT JOIN atp_surfaces AS asu ON asu.surfaces_id = m2s.surfaces_id
LEFT JOIN atp_matchs_to_tournament_type AS m2t ON m2t.matchs_id = am.matchs_id
LEFT JOIN atp_tournament_type AS att ON att.tournament_type_id = m2t.tournament_type_id
However, if one match can be played on one surface only and within one tournament type only, I would change your schema to:
Tables atp_matchs_to_surfaces and atp_matchs_to_tournament_type are removed and fields surfaces_id and tournament_type_id moved to atp_matchs table. Your query is now:
SELECT am.*, asu.surface_name, att.tournament_type_name
FROM atp_matchs AS am
LEFT JOIN atp_surfaces AS asu ON asu.surfaces_id = am.surfaces_id
LEFT JOIN atp_tournament_type AS att ON att.tournament_type_id = am.tournament_type_id
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2).
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN (SELECT att.tournament_type, am.*
FROM atp_matchs_to_tournament_type m2s) as......
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN atp_surfaces asf ON m2s.surfaces_id = asf.surfaces_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id
LEFT JOIN(
SELECT att.tournament_type, am.*
FROM atp_matchs_to_tournament_type m2s
LEFT JOIN atp_tournament_type att AS Q1 ON m2s.surfaces_id = att.surfaces_id
LEFT JOIN atp_matchs am AS Q2 ON am.matchs_id = m2s.matchs_id);
I added some "AS" because I had the error: Every derived table must have its own alias. I'm a little lost here!
So this is my query:
SELECT SQL_CALC_FOUND_ROWS wagons.id,
wagons.mid,
wagons.year,
wagons.make,
wagons.model,
wagons.nickname,
wagons.description,
members.first,
members.last,
wagon_photos.filename,
Count(DISTINCT( likes.id )) AS likes,
Count(DISTINCT( comments.id )) AS comments
FROM wagons
INNER JOIN members
ON members.mid = wagons.mid
LEFT JOIN wagon_photos
ON wagon_photos.wid = wagons.id
LEFT JOIN likes
ON likes.wid = wagons.id
LEFT JOIN comments
ON comments.wid = wagons.id
GROUP BY wagons.id
ORDER BY wagons.id DESC
LIMIT 10
I am trying to fetch the wagon_photos.filename where wagon_photos.default is the maximum value. (It's a boolean, where only one row (for each wagon_photos.wid) will be 1.) On the off-chance the member does not have a default photo selected, I'd like it to return the lowest wagon_photos.id if possible.
I have tried numerous queries with ORDER BY and GROUP BY, but I think it may be a little more complicated than that. If I use a WHERE clause, it left out any wagons that did not have any photos linked to it, which I do not want to do.
CREATE TABLE `wagons` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`mid` int(8) DEFAULT NULL,
`year` varchar(4) DEFAULT NULL,
`make` varchar(50) DEFAULT NULL,
`model` varchar(100) DEFAULT NULL,
`nickname` varchar(200) DEFAULT NULL,
`description` mediumtext,
`featured` int(1) DEFAULT '0',
`visibility` int(1) DEFAULT '1',
`commision` int(1) DEFAULT '1',
`feat1title` varchar(50) DEFAULT NULL,
`feat2title` varchar(50) DEFAULT NULL,
`feat3title` varchar(50) DEFAULT NULL,
`feat4title` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=617 DEFAULT CHARSET=utf8;
CREATE TABLE `wagon_photos` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`wid` int(11) DEFAULT NULL,
`filename` varchar(255) DEFAULT NULL,
`caption` varchar(255) DEFAULT '',
`default` int(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;
Let me try and understand what you're asking. I think what you're asking is "how do I construct a query where I want to select all of the items that have the max value of a particular column". If that's the case, you want to break this down into simpler units in order to solve it. First, you'll need the max value itself:
select max(wp.default) as max_val from wagon_photos wp
This gets you the max value.
Next you can create a query to select rows where a condition is true, but leave the condition out for a moment:
select (your rows) from wagons w where (a condition will go here)
And your condition is that you want wagon_photos.default to equal the max value. So you'll need to join in that table:
select (your rows) from wagons w join wagon_photos wp
on wp.wid = w.id where wp.default = (something)
Your "something" happens to be that max_value that we created a query for in the beginning. So putting this together, you'll use a subquery which will look like this:
select (your rows) from wagons w join wagon_photos wp
on wp.wid = w.id where wp.default = (
select max(wpb.default) from wagon_photos wpb
)
I haven't set up the tables myself to run this, so I might have goofed on parentheses, and it doesn't include all your joins. But I think this should point you in the right direction.
Well I feel a little bit silly now.
I finally thought of adding AND wagon_photos.default = 1 after the LEFT JOIN for wagon_photos and it works. I just need to make sure they always have a default setup, so I ran a script that on every update to their photos, it checks for a default, and if one isn't set, set the newest upload as default.
... LEFT JOIN wagon_photos ON wagon_photos.wid = wagons.id AND wagon_photos.default = 1 ...
I am working on a property website and have record sets for property and for unit, unit has a one-to-many relationship with property. What I'm trying to figure out is how to best create a search function which will output results based on criteria from both. So if I search for a property with the location Manchester and a unit with a freehold tenure I'd like to eliminate all properties which don't have a unit with the tenure of freehold.
A potential solution I've considered is to create a record set for properties which match the property criteria and then create a unit record set for units which match the unit criteria and then finally loop through the property record set in server-side code and eliminate any properties which aren't related to any of the units in the unit record set. Really not sure if this is the best way to do things though so would be keen to hear any suggestions?
Thanks
EDIT (Added table structure and MySQL):
--
-- Table structure for table `property`
--
CREATE TABLE IF NOT EXISTS `property` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`street` text NOT NULL,
`town` text NOT NULL,
`postcode` text NOT NULL,
`description` longtext NOT NULL,
`team_member` varchar(255) NOT NULL DEFAULT '',
`pdf` text NOT NULL,
`default_image_id` int(11) DEFAULT NULL,
`virtual_tour_link` text NOT NULL,
`date` date NOT NULL DEFAULT '0000-00-00',
`archive` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='' AUTO_INCREMENT=13 ;
--
-- Table structure for table `unit`
--
CREATE TABLE IF NOT EXISTS `unit` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`description` text NOT NULL,
`size_sq_ft` int(11) DEFAULT NULL,
`size_acres` float DEFAULT NULL,
`price` float DEFAULT NULL,
`rental_price` float DEFAULT NULL,
`on_application` tinyint(1) DEFAULT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stores data for property units' AUTO_INCREMENT=5;
--
-- Table structure for table `property_to_unit`
--
CREATE TABLE IF NOT EXISTS `property_to_unit` (
`property_id` int(11) NOT NULL,
`unit_id` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- MySQL which produces list of properties
--
SELECT
P.id AS id,
P.name AS name,
P.street AS street,
P.town AS town,
P.postcode AS postcode,
P.description AS description,
P.team_member AS team_member,
P.pdf AS pdf,
P.virtual_tour_link AS virtual_tour_link,
P.date AS date,
P.archive AS archive,
PI.name as image,
P2.image_ids as image_ids,
L2.location_ids as location_ids,
U2.unit_ids as unit_ids
FROM property P
-- Get default image and join using property id
LEFT JOIN property_image PI ON PI.id = P.default_image_id
-- Create a list of image_ids from property_image and
-- property_to_property_image tables then join using property_id
LEFT JOIN (
SELECT
property_id,
GROUP_CONCAT(CAST(id AS CHAR)) as image_ids
FROM property_to_property_image PTPI
LEFT JOIN property_image PI ON PI.id = PTPI.property_image_id
GROUP BY property_id
) P2 ON P2.property_id = P.id
-- Create a list of locations from property_location table
-- and join using property_id
LEFT JOIN (
SELECT
property_id,
property_location_id,
GROUP_CONCAT(CAST(property_location.id AS CHAR)) AS location_ids
FROM property_to_property_location
INNER JOIN property_location ON property_location.id = property_to_property_location.property_location_id
GROUP BY property_id
) L2 ON L2.property_id = P.id
-- Create a list of units from unit table
-- and join using property_id
LEFT JOIN (
SELECT
property_id,
unit_id,
GROUP_CONCAT(CAST(unit_id AS CHAR)) AS unit_ids
FROM property_to_unit
INNER JOIN unit ON unit.id = property_to_unit.unit_id
GROUP BY property_id
) U2 ON U2.property_id = P.id
--
-- MySQL which produces list of units
--
SELECT
id,
name,
description,
size_sq_ft,
size_acres,
price,
rental_price,
on_application,
tenure_ids,
tenure_names,
type_ids,
type_names
FROM unit AS U
-- join tenure ids and names
LEFT JOIN (
SELECT
unit_id,
GROUP_CONCAT( CAST(UT.id AS CHAR) ) AS tenure_ids,
GROUP_CONCAT(UT.name) AS tenure_names
FROM unit_to_unit_tenure UTUT
INNER JOIN unit_tenure UT ON UT.id = UTUT.unit_tenure_id
GROUP BY unit_id
) UT ON UT.unit_id = U.id
-- join type ids and names
LEFT JOIN (
SELECT
unit_id,
GROUP_CONCAT( CAST(UTYPE.id AS CHAR) ) AS type_ids,
GROUP_CONCAT(UTYPE.name) AS type_names
FROM unit_to_unit_type UTUT
INNER JOIN unit_type UTYPE ON UTYPE.id = UTUT.unit_type_id
GROUP BY unit_id
) UTYPE ON UTYPE.unit_id = U.id
WHERE 0=0
I'm currently using a dynamically created WHERE statement appended to each MySQL query to filter the property and unit results.
You're making it a bit more complicated than it is. If I understand correctly, you can easily do this in a single query. This would search properties that have units with a particlar unit tenure id:
select *
from property p
where p.id in (
select pu.property_id
from property_to_unit pu
inner join unit u ON pu.unit_id = u.id
inner join unit_to_unit_tenure uut ON u.id = uut.unit_id
where uut.id = <cfqueryparam value="#uutid#">
)
Using two queries and then looping through to cross-check sounds like it could be dog slow.
Your situation requires a posted foreign key in the property table. Store the unit_id in the property table and use a join in your query such as:
select * from property p, unit u
where p.unit_id = u.id
and p.town = ....
EDIT: So I just noticed the rest of your SQL. If you require to keep the many-to-many relationship table for the unit -> property relationship then you will need to join unit and property off of that table.