I have the following query :
SELECT A.id FROM logsen_alertes A
WHERE
( SELECT LA2.type_colocation_id
FROM logsen_liaisons_annonces_types_colocations LA2
WHERE LA2.annonce_id = 25 AND LA2.annonce_type = 4
)
IN
( SELECT L4.souhait
FROM logsen_liaisons_alertes_souhaits L4
WHERE L4.alerte_id = A.id
)
This query works well when my first subquery returns only 1 value, because that's how works IN(), looking for 1 unique value in a set of values. When my 1st subquery returns 2 or more values, MySQL returns me "Subquery returns more than 1 row". How can I make my query works when the first subquery returns several values ? Something like "WHERE [any of these values] i found in [ny of these values]" ?
Try:
SELECT DISTINCT A.id FROM logsen_alertes A
JOIN logsen_liaisons_alertes_souhaits L4 ON L4.alerte_id = A.id
JOIN logsen_liaisons_annonces_types_colocations LA2
ON LA2.type_colocation_id = L4.souhait AND LA2.annonce_id = 25 AND LA2.annonce_type = 4
Try this:
SELECT
A.id
FROM
logsen_alertes A
INNER JOIN logsen_liaisons_alertes_souhaits L4
ON L4.alerte_id = A.id
INNER JOIN logsen_liaisons_annonces_types_colocations LA2
ON LA2.type_colocation_id = L4.souhait
WHERE
LA2.annonce_id = 25 AND LA2.annonce_type = 4
SELECT DISTINCT A.id FROM logsen_alertes AS A
INNER JOIN logsen_liaisons_alertes_souhaits AS L4
ON (L4.alerte_id = A.id)
INNER JOIN logsen_liaisons_annonces_types_colocations AS LA2
ON (LA2.type_colocation_id = L4.souhait)
WHERE LA2.annonce_id = 25 AND LA2.annonce_type = 4
Should work
Related
the first picture is the table . second picture is the expected output.
conditions are 1. refids should be same. 2. for all the same ref ids (a.start,a.end &b.start,b.end) in the current and previous row. 3. should calculate the time difference which is greater than or equal to one day.
You want pairs of rows that match certain condition. You can perform a join to identify the pairs.
You don't say which version of MySQL you are using but in MySQL 8.x you can do:
with
x as (
select a.id
from my_table a
join my_table b on b.id = a.id + 1
and b.refid = a.refid
and (a.detail = 'a.end' and b.detail = 'a.start'
or a.detail = 'b.end' and b.detail = 'b.start')
)
select t.*
from my_table t
join x on t.id = x.id or t.id = x.id + 1
For MySQL 5.x you can do:
select t.*
from my_table t
join (
select a.id
from my_table a
join my_table b on b.id = a.id + 1
and b.refid = a.refid
and (a.detail = 'a.end' and b.detail = 'a.start'
or a.detail = 'b.end' and b.detail = 'b.start')
) x on t.id = x.id or t.id = x.id + 1
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 ran into a problem that my mysql query is not working as expected. I am joining 3 tables using union and from the result i want to ignore the repeating words. For that I have used DISTINCT keyword. But it is not working as expected.
My query is,
SELECT DISTINCT(catnam), sub2id
FROM tbl_first_page_products f INNER JOIN subcategory_level2 s
ON f.brand_id = s.sub2id
WHERE f.title_cat = 'men'
UNION
SELECT DISTINCT(catnam), sub2id
FROM tbl_third_page_products f INNER JOIN subcategory_level2 s
ON f.brand_id = s.sub2id
WHERE f.title_cat = 'men'
UNION
SELECT DISTINCT(catnam), sub2id
FROM tbl_fourth_page_products f INNER JOIN subcategory_level2 s
ON f.brand_id = s.sub2id
WHERE f.title_cat = 'men'
And my result is,
catnam sub2id
------ ------
Levi's 4
United 1
Reebok 130
Jack 18
Proline 77
Levi's 161
Arrow 284
In the above result Levi's is repeating. How to ignore this. I need only one Levi's. Is there any way to avoid this. I am stuck in here.
MySQL DISTINCT with multiple columns
You can use the DISTINCT clause with more than one column. In this case, MySQL uses the combination of all columns to determine the uniqueness of the row in the result set.
http://www.mysqltutorial.org/mysql-distinct.aspx
if you want solve your problem, you can use group_concat() function then you group by catnam for all union queries :
SELECT name ,GROUP_CONCAT(id)
FROM your_table
GROUP BY name ;
so your query can be:
SELECT distinct(catnam) as catnam, GROUP_CONCAT(sub2id )
FROM tbl_first_page_products f INNER JOIN subcategory_level2 s
ON f.brand_id = s.sub2id
WHERE f.title_cat = 'men'
UNION
SELECT distinct(catnam) as catnam, GROUP_CONCAT(sub2id )
FROM tbl_third_page_products f INNER JOIN subcategory_level2 s
ON f.brand_id = s.sub2id
WHERE f.title_cat = 'men'
UNION
SELECT distinct(catnam) as catnam, GROUP_CONCAT(sub2id )
FROM tbl_fourth_page_products f INNER JOIN subcategory_level2 s
ON f.brand_id = s.sub2id
WHERE f.title_cat = 'men'
group by catnam
I'd try:
SELECT n, GROUP_CONCAT(sub2id )
FROM
(
SELECT catnam n, sub2id
...
UNION
SELECT catnam, sub2id
...
)
GROUP BY n
I have the below query to find the row prior to MAX row. i feel like i am missing something, can somebody please help with it. I ammlooking forward to get the b.usercode_1 as row prior to a.usercode_1 not the min or any other random row but the ROW prior to the MAX.
Please suggest.
Select distinct
c.ssn
, c.controlled_group_Status CG_status
, c.last_name || ' , '|| c.first_name FULL_NAME
, a.usercode_1 Current_REG
, a.eff_date effective_since1
, b.usercode_1 PRIOR_REG
, b.eff_date effective_since2
, d.term_eff_date
from employee_eff_date c
, emp_cg_data a
, emp_cg_data b
, emp_ben_elects d
where c.control_id = 'XYZ'
and c.controlled_group_Status <> 'D'
and c.eff_date = (select max( c1.eff_date)
from emp_cg_data c1
where c.control_id = c1.control_id
and c.ssn = c1.ssn)
and a.control_id = c.control_id
and a.ssn = c.ssn
and a.eff_date = (select max(a1.eff_date )
from emp_cg_data a1
where a.control_id = a1.control_id
and a.ssn = a1.ssn)
and a.usercode_1 = 'REG26'
and b.control_id = c.control_id
and b.ssn = c.ssn
and b.eff_date = (select max( b1.eff_date)
from emp_cg_data b1
where b.control_id = b1.control_id
and b.ssn = b1.ssn
and b1.eff_date < a.eff_date)
and b.usercode_1 like 'REG%'
and d.control_id = c.control_id
and d.ssn = c.ssn
and d.life_event_date = (select max( d1.life_event_date)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn)
and d.le_seq_no= (select max( d1.le_seq_no)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn
and d.life_event_date = d1.life_event_date)
and d.term_eff_date is null
;
NOTE: this is not a complete answer... its a helpful suggestion of what you should start with.
you are doing a Cartesian Product of the four tables, filtered by a WHERE... so something like this
Implicit Join -- generally not a good practice as it can be very difficult to keep the where filters apart from the join conditions.
SELECT *
FROM tableA a, TableB b
WHERE b.id = a.id
another way to write a JOIN (the more generally accepted way)
SELECT *
FROM tableA a
JOIN tableB b ON b.id = a.id
Use the ON clause to join two tables together.
You should change your joins to this format so that others can read your query and understand it better.
suggestion to solve your problem
a fairly simple way to get the second to last row is to use a row counter.
so something like
SELECT *, #row_count := #row_count + 1
FROM tableA a
JOIN tableB b on b.id = a.id AND -- any other conditions for the join.
CROSS JOIN (SELECT #row_count := 0) t
then from here you can get the MAX row, whether thats the ID or something else. and then get the #row_num -1. aka the previous row.
I am having a bit of a brain block with this problem and I am finding it hard to search for a solution because I cant phrase the question correctly to bring up the relevant information.
I am trying to get back "fProduct" record from the table below where it has a "fAttribute"
of 2 and 20.
id fAttribute fProduct
19 2 2967
48 2 2923
50 2 3008
51 20 3008
52 2 2295
53 20 2295
My statment below produces 0 results when I would expect to return fProduct's 2295 and 3008.
SELECT fProduct
FROM tableName
WHERE fAttribute = 2 AND fAttribute = 20
GROUP BY fProduct
Can anyone help please?
You can either use INNER JOINS or use EXISTS conditions:
INNER JOIN:
SELECT DISTINCT a.fProduct
FROM MyTable a
INNER JOIN MyTable b ON a.fProduct = b.fProduct AND b.fAttribute = 2
INNER JOIN MyTable c ON a.fProduct = c.fProduct AND c.fAttribute = 20
EXISTS:
SELECT afproduct
FROM MyTable a
WHERE EXISTS (SELECT b.id FROM MyTable b WHERE a.fProduct = b.fProduct AND b.fAttribute = 2)
AND EXISTS (SELECT c.id FROM MyTable c WHERE a.fProduct = c.fProduct AND c.fAttribute = 20)
A join should help:
SELECT distinct a.fProduct
FROM tableName as a
join tableName as b on b.product = a.product
WHERE a.fAttribute = 2 and b.fAttribute = 20
Since your are already doing a GROUP BY just change your WHERE clause to an OR or an IN and add the HAVING COUNT(fattribute) = 2 which makes sure it has both.
SELECT fproduct
FROM tablename
WHERE fattribute IN (2 , 20)
GROUP BY fproduct
HAVING COUNT(fattribute) = 2