Check if there are multiple values in a column - mysql

I have the following sql:
SELECT DISTINCT cp_pessoa.id, cp_pessoa.nome
FROM cp_pessoa
LEFT JOIN cp_habilidade_freelancer ON (cp_habilidade_freelancer.id_freelancer = cp_pessoa.id)
LEFT JOIN cp_habilidade ON (cp_habilidade.id = cp_habilidade_freelancer.id_habilidade)
WHERE cp_habilidade.id = 71 OR cp_habilidade.id = 695
LIMIT 0, 10
I only want people (cp_pessoa) who have all the skills (71, 695).
It may seem simple, but I'm struggling.
Examples:
If I use OR the following people with the following skills (1,2,71) are returned (people without the ability 695)
If I use AND the following people with the following skills (71, 695) are not returned

Is this homework? If so, the idea should suffice: You are looking for persons where the count of the associtated skills 71 and 695 is 2.
In case this is no homework, but a real task, then tell me so and I'll give you a complete SQL statement :-)
EDIT: The straight-forward way is to ask if both skills exist for a person:
select id, nome
from cp_pessoa p
where exists
(
select *
from cp_habilidade_freelancer hf
where hf.id_freelancer = p.id
and hf.id_habilidade = 71
)
and exists
(
select *
from cp_habilidade_freelancer hf
where hf.id_freelancer = p.id
and hf.id_habilidade = 695
);
The other way with querying cp_habilidade_freelancer just once:
select id, nome
from cp_pessoa p
where id in
(
select id_freelancer
from cp_habilidade_freelancer hf
where id_habilidade in (71, 695)
group by id_freelancer
having count(distinct id_habilidade) = 2
);
You could change count(distinct id_habilidade) to count(*) in case it is guaranteed that there are no duplicate skills for an id_freelancer in cp_habilidade_freelancer.

Related

SQL Query behavior

I'm bogged in trying to figure out why query a is returning different records than query b. Both queries have seemingly same purpose yet a is returning 500 and b 3500.
this is query a:
SELECT DISTINCT ODE.OrderBillToID
FROM APTIFY.dbo.vwVwOrderDetailsKGExtended ODE
WHERE ProductID IN (2022, 1393)
AND LTRIM(RTRIM(ODE.OrderStatus)) <> 'Cancelled'
AND LTRIM(RTRIM(ODE.OrderType)) <> 'Cancellation'
AND LTRIM(RTRIM(ODE.cancellationStatus)) <> 'FULLY CANCELLED'
UNION
SELECT DISTINCT ID
FROM APTIFY.dbo.vwPersons WHERE City = 'A'
UNION
SELECT DISTINCT RecordID
FROM APTIFY.dbo.vwTopicCodeLinks WHERE TopicCodeID = 16 AND Value = 'Yes, Please'
query b:
SELECT
APTIFY..vwPersons.ID
FROM
APTIFY..vwPersons
WHERE
( APTIFY..vwPersons.ID IN (
SELECT
vwMeetingRegistrants.ID
FROM
APTIFY.dbo.vwMeetings vwMeetings
INNER JOIN APTIFY.dbo.vwMeetingRegistrants vwMeetingRegistrants
ON vwMeetings.ID=vwMeetingRegistrants.ActualMeetingID WHERE
vwMeetings.ProductID = 2022
)
OR
APTIFY..vwPersons.ID IN (
SELECT
vwMeetingRegistrants.ID
FROM
APTIFY.dbo.vwMeetings vwMeetings
INNER JOIN APTIFY.dbo.vwMeetingRegistrants vwMeetingRegistrants
ON vwMeetings.ID=vwMeetingRegistrants.ActualMeetingID WHERE
vwMeetings.ProductID = 1393
)
OR
APTIFY..vwPersons.City = N'Albany' )
OR
((
APTIFY..vwPersons.ID IN (
SELECT
RecordID
FROM
APTIFY.dbo.vwTopicCodeLinks vwTopicCodeLinks
WHERE
vwTopicCodeLinks.TopicCodeID = 16
)
AND
APTIFY..vwPersons.ID IN (
SELECT
RecordID
FROM
APTIFY.dbo.vwTopicCodeLinks vwTopicCodeLinks
WHERE
vwTopicCodeLinks.Value = N'Yes, Please'
) )
)
vwMeetingsRegistrants from the b query are producing the same records as orderkgdetailsextended from query. I cannot see ANY difference in those queries - which perhaps shows my lack of understanding the query behaviour.
BIG Thanks for any points guys! :)
As it came out, incorrectly structured query is a result of badly configured application, Aptify.

mysql SELECT EXISTS on multiple tables

Have tables: person,person_ip
Both tables have pid column as a primary key, in table person there is column state_id, in table person_ip there is column ip.
Want to discover if specified IP address is assigned to person with state_id is not equal to 2. But always got result 1, even if state_id is 0, 1 or 2. Always got 0 only if ip address is not listed at all. What am I doing wrong?
SELECT EXISTS (
SELECT person_ip.PID
FROM person_ip,person
WHERE person.PID=person_ip.PID
AND person.state_id NOT IN (2)
AND person_ip.ip='10.11.12.13'
)
this seems like a simple join.. unless i'm missing something
select person.*
from person
inner join person_ip
on person.pid = person_ip.pid
where person.state_id <> 2
and person_ip.ip_address = '10.0.0.1'
If you want to exclude the ip_address if it has been assigned to any user with state = 2, even if it has also been assigned to a user without state = 2, then try:
select max(i)
from (
select *
from (
select 1 as i
from dual
where not exists (
select 1
from person p
inner join person_ip pi
on p.pid = pi.pid
where p.state_id = 2
and pi.ip_address = '10.0.0.1'
)
) q
union
select 0
) qq
(dual is a system table that can be used as a sort of stub table)
here's a fiddle showing both versions
update after some actual sleep
Okay, so the above query is a little.. out there. Back in the real world, this one is probably more appropriate:
select count(case when p1.state_id = 2 then 1 end)
from person p1
inner join person_ip pi1
on p1.pid = pi1.pid
where pi1.ip_address = '10.0.0.1'
group by pi1.ip_address;
This will return 1 or more if your ip_address has been used by someone with a state_id of 2, and 0 if it has never been used by someone with a state_id of 2.
It will return nothing if the ip has never been used.
this fiddle has all three of the above queries.
SELECT IF(COUNT(*)>0,1,0)
FROM person
INNER JOIN person_ip
ON person.pid = person_ip.pid
AND person_ip.ip_address = '10.0.0.1'
WHERE person.state_id <> 2

SELECT if left join IS AND IS NOT ( MYSQL )?

I got one table "people".
Another one with "hobbiesConnection", and they are connected as an OneToMany.
I would like to build a search, where it is possible to search people which have a hobby and don't have a hobby.
Something like :
SELECT * FROM people
LEFT JOIN hobbiesConnection ON hobbiesConnection.people_id =
hobbiesConnection.hobby_id
WHERE hobby_id = 4 AND hobby_id != 5
Now I get the person with the hobby 4, but I want to get an empty result, because both conditions have to fit to the person.
Does anybody have an idea how to accomplish this ?
Firstly, your JOIN is not correct. The two tables should be linked with a foreign key / common column. Try the below query;
SELECT * FROM people
LEFT JOIN hobbiesConnection
ON hobbiesConnection.people_id = people.people_id
And (hobbiesConnection.hobby_id = 4 AND hobbiesConnection.hobby_id != 5)
use (NOT) EXISTS clause:
SELECT * FROM people p
WHERE TRUE
AND EXISTS (
SELECT TRUE FROM hobbiesConnection hc
WHERE hc.people_id = p.people_id -- or p.id? you didnt mention
AND hobby_id = 4
)
AND NOT EXISTS (
SELECT TRUE FROM hobbiesConnection hc
WHERE hc.people_id = p.people_id -- or p.id? you didnt mention
AND hobby_id = 5
)
You can achieve the same using joins
SELECT p.*
FROM people p
JOIN hobbiesConnection h4 ON TRUE
AND h4.people_id = p.people_id -- or p.id? you didnt mention
AND h4.hobby_id = 4
LEFT JOIN hobbiesConnection h5 ON TRUE
AND h5.people_id = p.people_id -- or p.id? you didnt mention
AND h5.hobby_id = 5
WHERE h5.id IS NULL -- or whatever column from that table
have a look at the fiddle: http://sqlfiddle.com/#!9/d9926/4

MySQL Database design advice - using joins

I am building an AJAX like search page which allows a customer to select a number filters that will narrow down the search. For instance, a user has selected an 'iPhone 5' and has additional filters for capacity (32GB, 64GB) & colour (black, white..).
The user can only select a single radio box per category (so they could select 32GB & Black).. but they could not select (32GB & 64GB & black as two of these belong to the 'capacity' category).
I have added the schema here on sqlfiddle (please ignore the fact i've removed the primary keys they exist in the proper app they have just been removed along with some other fields/data to minimise the sqlfiddle)
http://sqlfiddle.com/#!2/964425
Can anyone suggest the best way to create the query to do the following:
Get all the prices for device_id '2939' (iPhone 5) which has the 'attributes' of '32GB' AND 'Black'
I currently have this - but this only works when selecting for a single attribute:
// search for device with '64GB' & 'Black' attributes (this currently doesn't return any rows)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19'
AND `attribute_option_id` = '47';
// search for device with '64GB' attribute only (this currently DOES return a row)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19';
Any advice on the database design would be appreciated too
Note: I was thinking to have a new column within the 'prices' table that has the matching attribute_ids serialised - would this be not good for optimisation however (e.g would it be slower than the current method)
Since attribute_option_id is an atomic value, it cannot have two different values for the same row. So your WHERE clause cannot match any record:
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
AND `attribute_option_id` = '19' # Here for one row, attribute_option_id is either 19
AND `attribute_option_id` = '47'; # of '47'. Cannot be the both
Instead of JOIN, you could try a subquery if you feel that is more readable. I think MySQL allow that syntax:
SELECT `prices`.*
FROM `prices`
WHERE `prices`.`device_id` = '2939'
AND EXISTS (SELECT *
FROM prices_attributes
WHERE price_id = `prices`.`id`
AND attribute_option_id IN ('19', '47') )
I don't know how MySQL will optimize the above solution. An alternative would be:
SELECT `prices`.*
FROM `prices`
WHERE `prices`.`id` IN (
SELECT DISTINCT `price_id`
FROM prices_attributes
WHERE attribute_option_id IN ('19', '47')
)
I think you should use the IN operator for the attribute_option_id and you set the values dynamically to the query; Also, using group_by you have only one row per price so in effect you get all the prices. Apart from this, the design is ok.
Here, I have made an example:
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` = '2939'
and `attribute_option_id` in ('19','47')
group by `prices`.`device_id`, `prices`.`price`;
Here, you can also add an order clause to order by price:
order by `prices`.`price` desc;
Another way to solve this would be to use a distinct on price, like this:
select distinct(prices.price)
from prices
where prices.device_id = 2939
and id in (select price_id from prices_attributes where attribute_option_id in (19,47));
Join against the devices_attributes_options table several times, once for each attribute the item must have
Something like this:-
SELECT *
FROM devices a
INNER JOIN prices b ON a.id = b.device_id
INNER JOIN prices_attributes c ON b.id = c.price_id
INNER JOIN devices_attributes_options d ON c.attribute_option_id = d.id AND d.attribute_value = '32GB'
INNER JOIN devices_attributes_options e ON c.attribute_option_id = e.id AND e.attribute_value = 'Black'
WHERE a.id = 2939
As to putting serialised details into a field, this is a really bad idea and would come back to bite you in the future!
SELECT * FROM prices WHERE device_id=2939 AND id IN (SELECT price_id FROM prices_attributes WHERE attribute_option_id IN (19,47));
Is it what you're looking for?
EDIT: sorry, didn't notice you're asking for query using joins

How do you do multiple inserts that have selects

How do I do an insert multiple values or records that have to get their information from select statements. This doesn't work.
INSERT INTO marriedcouples (male,female) VALUES (
(SELECT id FROM men WHERE username='brad',
SELECT id FROM women WHERE username='jennifer')
(SELECT id FROM men WHERE username='ken',
SELECT id FROM women WHERE username='barbie'))
Assuming I have tables with:
men(id,name), women(id,name), couples(id,male,female)
etc.
Thanks,
Dan
Insert marriedcouples( male, female )
Select M.id, W.id
From Men As M
Cross Join Women As W
Where ( M.username = 'brad' And W.username = 'jennifer' )
Or ( M.username = 'ken' And W.username = 'barbie' )
Addition
In comments, you asked specifically about the problems with your original query. First, you could have used your original approach like so:
Insert marriedcouples( male, female )
Select ( Select Id From men Where username = 'brad' )
, ( Select Id From women Where username = 'jennifer' )
Union All
Select ( Select Id From men Where username = 'ken' )
, ( Select Id From women Where username = 'barbie' )
Notice that each value is enclosed in parentheses as its own encapsulated subquery. Second, notice that I used the Union All directive to allow me to stack the two queries and give me two rows. Third, notice that I'm not trying to use the Values directive in combination with subqueries. You can use the Values clause to list out values or you can use a Select statement but not both in the way you did. Obviously, this approach of four subqueries will not perform well but it helps to understand the breakdown of the syntax.