I have a table with text in various language. Its defined like this:
Id|Language|Text
EXAMPLE DATA
0, ENU, a
0, DAN, b
1, ENU, c
2, ENU, d
2, DAN, e
3, ESP, f
3, ENU, g
Language and Id form the key.
Now I want to extract all texts in a langauge (lets say english) and have the coorosponding text in another language (lets say danish) shown in the column next to. So the result should be:
0, a, b
1, c,
2, d, e
3, g
I know I can do a join like this:
SELECT t1.Id, t1.Text AS "ENU", t2.Text AS "DAN" table as t1
JOIN table as t2 ON (t1.Id= t2.Id)
WHERE t1.Langauge = "ENU" AND t2.Language = "DAN";
But this does not include the missing rows (ie row id=1 and id=3). How to do this?
* UPDATE ****
I get suggestion to use LEFT JOIN but I cant get it working. Maybe because my table layout is a bit different than in the simplified question above. My table is defined as this:
Language|MPageId|MFieldId|MParagraph|MText
Where Language,MPageId,MFieldId,MParagraph forms the key
I tried this:
SELECT t1.MPageId, t1.MFieldId, t1.MParagraphId, t1.MText, t2.MText
FROM main as t1 LEFT JOIN main as t2 ON (t1.MPageId = t2.MPageId AND
t1.MFieldId = t2.MFieldId AND t1.MParagraphId = t2.MParagraphId) WHERE
t1.MLanguage = 'ENU' AND t2.MLanguage = 'DAN'
SELECT t1.Id, t1.Text AS "ENU", t2.Text AS "DAN" FROM table as t1
LEFT JOIN table as t2 ON (t1.Id= t2.Id AND t2.Language = "DAN")
WHERE t1.Langauge = "ENU"
You do need the left join... but the "AND" clause for "DAN"ish would be applied AT the LEFT JOIN, and not in the WHERE clause... The where clause implies an INNER JOIN
SELECT
t1.Id,
t1.Text AS "ENU",
t2.Text AS "DAN"
from
YourTable t1
LEFT JOIN YourTable t2
ON t1.Id= t2.Id
AND t2.Language = "DAN"
where
t1.Langauge = "ENU"
You want a Left Join: http://www.tizag.com/mysqlTutorial/mysqlleftjoin.php
select Id,
MAX(case when LanguageS='ENU' then Text else null end ) as A,
MAX( case when LanguageS<>'ENU' then Text else null end ) as B
from LAN
GROUP BY 1
Id A B
0 a b
1 c ?
2 d e
3 g f
Related
I have the following SQL query that is bottlenecking at some point. It will make it halfway through around 900 records it returns before timing out. When I change my second select to only return certain columns it helped a bit but it is still bottlenecking somewhere.
I'm thinking there is something wrong with the way that I have the JOINs written but could use any advice.
SELECT * FROM
(SELECT * FROM TABLE1 B
INNER JOIN TABLE2 A ON B.ID_NBR = A.ID_NBR
INNER JOIN TABLE3 L ON A.CUST_NUM = L.CUST_NUM
WHERE B.SUPP_NBR = '17' AND STAT_NUM <> 4 AND BGHT_ID is not null
AND
NOT EXISTS (SELECT 1 FROM TABLE4 A WHERE A.ID_NBR = B.ID_NBR)) APPL
INNER JOIN TABLE5 ON M.PRSN_NUM = PRSN_NUM
WHERE M.LOC_ID = 'US' and (APPL.STAT_CD <> 'S' OR (APPL.STAT_CD =
'S' AND M.TXT_LOC !=
'UNKNOWN'));
UPDATE: I ended up using a LEFT JOIN instead of NOT EXISTS, changed the SELECT *, and used GROUP BY to correct the indexing issue:
SELECT * FROM
(SELECT B.ID_NBR, B.SUPP_NBR, B.STAT_NUM, B.BGHT_ID, A.CUST_NUM,
L.CUST_NUM, L.STAT_CD FROM TABLE1 B
INNER JOIN TABLE2 A ON B.ID_NBR = A.ID_NBR
INNER JOIN TABLE3 L ON A.CUST_NUM = L.CUST_NUM
LEFT JOIN TABLE4 C ON C.ID_NBR = B.ID_NBR
WHERE C.ID_NBR is null AND B.SUPP_NBR = '17' AND B.STAT_NUM <> 4
AND B.BGHT_ID is not null
GROUP BY B.ID_NBR, B.SUPP_NBR, B.STAT_NUM, B.BGHT_ID, A.CUST_NUM,
L.CUST_NUM, L.STAT_CD)
APPL
INNER JOIN TABLE5 ON M.PRSN_NUM = PRSN_NUM
WHERE M.LOC_ID = 'US' and
(APPL.STAT_CD <> 'S' OR M.TXT_LOC != 'UNKNOWN');
Can't this
(APPL.STAT_CD <> 'S'
OR (APPL.STAT_CD = 'S' AND M.TXT_LOC != 'UNKNOWN')
)
be simplified to
(APPL.STAT_CD <> 'S' OR M.TXT_LOC != 'UNKNOWN')
(It probably won't speed up the query much.)
Some of these indexes may help:
M: INDEX(LOC_ID, TXT_LOC, PRSN_NUM)
APPL: INDEX(STAT_CD)
B: INDEX(SUPP_NBR, ID_NBR)
A: INDEX(ID_NBR, CUST_NUM)
L: INDEX(CUST_NUM)
I have two tables.
table_a:
id | data_x | data_y
--------------------
1 person joe
2 person bob
3 amount 200
4 addres philville
tableB:
map_id | table_a_id
-------------------
7 1
7 3
7 4
8 4
8 2
The result I want is the map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
So with the above table B, the result should be
map_id
------
7
How can I write that query in SQL?
This situation is a perfect fit for an unusual SQL operator: INTERSECT. It is a very declarative, efficient and elegant solution for this problem.
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Person ON (Person.id = Map.table_a_id) AND (Person.data_x = 'person')
INTERSECT
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Amount ON (Amount.id = Map.table_a_id) AND (Amount.data_y = '200')
Formally what you are asking for is exactly the intersection of two disjoint sets: the set of map id's that are persons and the set of map id's that have a value of 200.
Please note the INTERSECT operator does not exists in MySQL, but it does in almost all advanced relational DBMS, including PostgreSQL.
This is less elegant than the INTERSECT solution #Malta posted, but it works with the limited capabilities of MySQL as well:
SELECT b1.map_id
FROM table_a a1
JOIN tableb b1 ON a1.id = b1.table_a_id AND a1.data_x = 'person'
JOIN tableb b2 ON b2.map_id = b1.map_id AND b2.table_a_id <> b1.table_a_id
JOIN table_a a2 ON a2.id = b2.table_a_id AND a2.data_y = '200';
SQL Fiddle for MySQL.
SQL Fiddle for Postgres.
Based on your input, the following should get you started using MySQL:
SELECT
map_id
FROM TableB
JOIN Table_A
ON TableB.table_a_id = Table_A.id
AND
((Table_A.data_x = 'person')
OR
(Table_A.data_y = '200')
)
GROUP BY map_id
HAVING COUNT(table_a_id) = 2
;
See it in action: SQL Fiddle.
Update
As Erwin Brandstetter made explicit: If the data can't be trusted to be inherently consistent (along the lines of your inquiry), one option is:
SELECT map_id FROM (
SELECT map_id, 'data_x' t
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y'
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2
;
This should ensure "at least one each". (Alternatives have been suggested by others.) To get "exactly one each", you could try
SELECT map_id FROM (
SELECT map_id, 'data_x' t, data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y', data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2 AND COUNT(DISTINCT data_y) = 2
;
See it in action (with additional test data): SQL Fiddle.
And it works in PostgreSQL as well: SQL Fiddle
Please comment if and as this requires adjustment / further detail.
Join the 2 tables, group by map_id, use conditional counting with either count() or sum(), and filter in having clause (I use mysql syntax below):
select map_id,
sum(
case
when a.data_x='person' or a.data_y='200' then 1
else 0
end
) as matches
from a
inner join b on a.id=b.a_id
group by b.map_id
having matches=2
The above query assumes that you cannot have more than one record for any map_id where data_x is person or data_y is 200. If this assumption is incorrect, then you need to use either exists subqueries or 2 derived tables.
Sounds like you want a standard INNER JOIN.
But I do beg to differ on your result:
map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
There is not a record in your data set that has both 'person' and data_y = '200' and therefore no mp_id can be returned
Here is a typical INNER JOIN relating to your narrative.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
AND a.data_y = '200'
If more than one map_id exists with data_x = 'person' and data_y = '200' then you will get multiple results but only 1 row per map_id
If you want the map_id(s) for records with data_x = 'person' or data_y = '200' then switch the and in the where statement to or and you will receive map_id 7 & 8.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
OR a.data_y = '200'
Note this encompasses (7,1)(8,2) because 1 & 2 both have data_x = 'person' and then (7,3) because 3 has data_y = '200' therefore it would return map_id 7 & 8.
select map_id from
table_b b
left outer join table_a a1 on (b.table_a_id = a1.id and a1.data_x = 'person')
left outer join table_a a2 on (b.table_a_id = a2.id and a2.data_y = '200')
group by map_id
having count(a1.id) > 0 and count(a2.id) > 0
Lets do it simple:
SELECT * FROM
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_x = 'person'
) as p
inner join
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_y = '200'
) as q
on p.map_id = q.map_id
You may replace SELECT * FROM with SELECT p.map_id FROM.
You may add more sub-set-joins to have more conditions.
sql-fiddle
I have 8 different tables and I would like to select only specific columns in one query. If I use this query I have wrong results :(
SELECT a.entity_id id
, a.field_imie_value imie
, c.field_nazwisko_value nazwisko
, d.field_preferencja_1_value preferencja1
, e.field_preferencja_2_value preferencja2
, f.field_preferencja_3_value preferencja3
, g.field_nr_niu_value nr_niu
, h.dystans odleglosc
, i.field_sytuacja_value sytuacja
FROM field_data_field_imie a
JOIN field_data_field_nazwisko c
, field_data_field_preferencja_1 d
, field_data_field_preferencja_2 e
, field_data_field_preferencja_3 f
, field_data_field_nr_niu g
, field_data_field_adres h
, field_data_field_sytuacja i
WHERE a.entity_id = b.entity_id
AND a.entity_id=c.entity_id
When you use joining tables you must use syntax similar to :
select A.field1 from table1 AS A inner join table2 as B on A.field1 = B.field1
syntax should be like this;
SELECT *
FROM field_data_field_imie a
JOIN field_data_field_nazwisko c on (a.entity_id=c.entity_id)
JOIN table x on (x.column=a.entitiy_id)
or you shouldn't use join at all.
actually number of tables is the important thing not how many fields you want however this is the syntax for joining more table :
elect A.field1 from table1 AS A inner join table2 as B on A.field1 = B.field1 inner join table3 AS C on table1.field1 = C.field1
and syntax for more joining is similar
I have a query as like
SELECT *
FROM aTable
LEFT JOIN aTableTranslate
ON aTable.id = aTableTranslate.aTable_id
WHERE
aTableTransalte.language like 'en'
question is ....
is there any way to filter as like
...
WHERE
aTableTranslate.language like (IF EXIST_A_FIELD_FOR 'en' THEN 'en' ELSE IF EXIST_A_FIELD_FOR 'jp' THEN 'jp' OR 'cn')
?
I want to show list with 1. visitor's language > 2. english ... > or default language.
is it possible by query?
You could join to translation tables separately, and pick the first non-NULL translation with COALESCE, like this:
SELECT a.*, COALESCE(t1.translation, t2.translation, t3.translation) as translation
FROM aTable a
LEFT JOIN aTableTranslate t1 ON aTable.id = t1.aTable_id AND t1.language like 'en'
LEFT JOIN aTableTranslate t2 ON aTable.id = t2.aTable_id AND t2.language like 'jp'
LEFT JOIN aTableTranslate t3 ON aTable.id = t3.aTable_id AND t3.language like 'cn'
Try this:
SELECT a.*, COALESCE(IF(t1.language = 'en', t1.translation, NULL),
IF(t1.language = 'jp', t1.translation, NULL),
IF(t1.language = 'cn', t1.translation, NULL)
) AS translation
FROM aTable a
LEFT JOIN aTableTranslate t1 ON aTable.id = t1.aTable_id AND t1.language IN ('en', 'jp', 'cn')
I have two tables that I believe I want to JOIN. I'm very new to this and am not completely sure…
The first table is called venues with the variables id, slug, name, etc. The second table is venue_terms with the variables id, option, venue, value. The matching variables are obviously venues.id and venue_terms.venue.
What I want to do is query venue_terms for matching values and then SELECT * FROM venues that match.
I've been working with the following query, but haven't been able to get it to work. I know INTERSECT isn't the solution, but I'm nut sure which JOIN I should use.
SELECT venue
FROM venue_terms
WHERE `option` = '1' AND `value` = '10'
INTERSECT
SELECT venue
FROM venue_terms
WHERE `option` = '2' AND `value` = '4';
I want to match those venue_terms.venue to the venues table. Can someone point me in the right direction?
UPDATE: To clarify, I'm trying to search multiple option/value combinations that ultimately have the same venue.id's. Basically, I want to able to find all of the venues where (option = 1 and value = 4) AND (option = 2 and value = 10) AND etc… where all of these are true.
You want to find venues that match conditions in two rows in table venue_terms. This can be accomplished by various methods. The most usual is by joining that table twice (another would be by a grouping query).
Here's the first way. Join twice to the venue_terms table:
SELECT v.id --- whatever columns you need
, v.slug --- from the venues table
, v.name
FROM venues AS v
INNER JOIN venue_terms AS vt1
ON vt1.venue = v.id
INNER JOIN venue_terms AS vt2
ON vt2.venue = v.id
WHERE ( vt1.option = 1 AND vt1.value = 10 )
AND ( vt2.option = 2 AND vt2.value = 4 ) ;
If you have 3 conditions, join thrice. If you have 10 conditions, join 10 times. It would be good for the efficiency of the query to have a compound index on (option, value, venue) in the terms table.
try this
SELECT venue.*, venue_terms.*
FROM venue
INNER JOIN venue_terms ON venue.id = venue_terms.venue
WHERE venue_terms.option IN ( 1 ,2)
AND venue_terms.value IN (10,4)
GROUP BY venue.id
How about this?
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE (t2.option = 1 AND t2.value = 10)
NOTE: I believe option and value are of type INT.
If they are of type varchar then change above query to
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE (t2.option = '1' AND t2.value = '10')
Update 1
As per your new requirement, you will just need to add that condition with OR option as shown below.
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE
(t2.option = 1 AND t2.value = 10)
OR
(t2.option = 3 AND t2.value = 14)
This will join the two tables and print out the venues which matches the attributes (option, value) in venue_terms:
SELECT v.* FROM venue v, venue_terms vt
WHERE v.id = vt.venue
AND vt.option = 1
AND vt.value = 10