I have a database with a one to many relationship (venues, with associated categories) like so:
venue_id | venue_name
---------------------
1 | venue1
2 | venue2
and categories
venue_id | category_id
---------------------
1 | 5
2 | 7
1 | 8
2 | 5
I want to show all venues that HAVE category_id of 5, but dont have category_id of 7 and 8. I tried using a join like so:
SELECT distinct(`venue_to_category`.`venue_id`),`venue_name`
FROM `venue_to_category` INNER JOIN `venues`
ON `venues`.venue_id = `venue_to_category`.venue_id
WHERE `category_id` != 7
AND `category_id` != 8
AND `category_id` = 5
But it is not returning the correct results (in fact I am unsure the results it is returning)
2 Things in your case
Get data where category_id = 5 and not 7 or 8
Get data where category_id = 5 and not both 7 and 8
For the first one you can use
select
v.venue_id,
v.venue_name
from venues v
join categories c on c.venue_id = v.venue_id
where c.category_id = 5
AND NOT EXISTS
(
select 1 from categories c1 where v.venue_id = c1.venue_id
AND c1.category_id in (7,8)
);
For the 2nd one
select
v.venue_id,
v.venue_name
from venues v
join categories c on c.venue_id = v.venue_id
where c.category_id = 5
AND NOT EXISTS
(
select 1 from categories c1 where v.venue_id = c1.venue_id
AND c1.category_id in (7,8) having count(*) = 2
);
DEMO
When doing a search function of your site. Match and Against is better than Like sql statement. The field must be set to FullText match the term on the field:
SELECT vc.venue_id, venue_name FROM venue_to_category vc, venues v WHERE MATCH(category_id) AGAINST('-7 -8 +5');
You can also use the IN BOOLEAN MODE to allow operators in the sql statement. eg. ...
MATCH(category_id) AGAINST('-7 -8 +5' IN BOOLEAN MODE) ...
(-) minus sign that means nothing should match '-7' '-8'
(+) the word must be present in the match.
There are many other operators to be used. Refer to this page for more operator and explanation
Try
SELECT distinct `venue_to_category`.`venue_id` ,`venue_name`
FROM `venue_to_category` INNER JOIN `venues`
ON `venues`.venue_id = `venue_to_category`.venue_id
WHERE `category_id` <> 7
AND `category_id` <> 8
AND `category_id` = 5
or
SELECT distinct `venue_to_category`.`venue_id` ,`venue_name`
FROM `venue_to_category` INNER JOIN `venues`
ON `venues`.venue_id = `venue_to_category`.venue_id
WHERE `category_id` NOT IN (7,8)
AND `category_id` = 5
Note: distinct is not a function. And for your example you could leave it out entirely, because there are no duplicate rows.
SELECT v.id, v.name
FROM venues v
JOIN venue_to_category vc
ON vc.venue_id=v.id
AND vc.category_id=5
WHERE NOT EXISTS (
SELECT 1
FROM venue_to_category vci
WHERE vci.venue_id = v.id
AND vci.category_id IN (7,8)
);
Related
Am looking for the help of some sql query experts.
Having some hard time for me to fix the issue in below sql statement. This is a join sql statement of 3 tables. and i need similar kind of data from people and alumni table. And the field name also same in both table. So my question is- is there any for me to get the similar data in a single field?
SELECT DISTINCT P.people_id, P.Name,P.Journal_name, N.People_id, N.Name, N.Journal_name
FROM `Paper_Author` AS A
LEFT JOIN `People` AS P ON ( A.Author_id = P.people_id )
LEFT JOIN `Alumni` AS N ON ( A.Author_id = N.People_id )
WHERE A.Paper_id =2067
ORDER BY A.Author_sortorder
LIMIT 0 , 30
Eg of Current Result is:
Id-- Name-- Journal_name-- ID2-- Name-- Journal_name
1 Name1 A1 NULL NULL NULL
2 Name2 B1 5 Name10 NULL
3 Name3 C1 3 Name3 C1
Expected Result :
Id-- Name-- Journal_name--
1 Name1 A1
2 Name2 B1
3 Name3 C1
5 Name10 NULL
I want to know whether i can get similar values in single filed? eg:both Journal_name in single field?
A UNION should work for this task. With a UNION statement you'll get both results in a single result set:
SELECT DISTINCT P.people_id, P.Name, P.Journal_name, AP.Author_sortorder
FROM `Paper_Author` AS AP
LEFT JOIN `People` AS P ON ( A.Author_id = P.people_id )
WHERE AP.Paper_id = 2067
UNION
SELECT DISTINCT N.People_id, N.Name, N.Journal_name, AN.Author_sortorder
FROM `Paper_Author` AS AN
LEFT JOIN `Alumni` AS N ON ( A.Author_id = N.People_id )
WHERE AN.Paper_id = 2067
ORDER BY Author_sortorder
So I have the table flag_counts with the following columns:
flag_id content_id
1 10
2 10
2 11
and the following query
SELECT flag_id, content_id FROM flag_counts
WHERE flag_id != 1
and of course it returns
flag_id content_id
2 10
2 11
How can I change my query so that it returns all content_ids that were not flagged with "1". In this example, I would like the query to return only the last row of the table since the content_id "10" was flagged once with "1".
Group by the content_id and take only those having zero records with flag_id = 1
SELECT content_id
FROM flag_count
GROUP BY content_id
HAVING sum(flag_id = 1) = 0
SELECT whatever
FROM flag_counts
WHERE content_id NOT IN (
SELECT content_id
FROM flag_counts
WHERE flag_id = 1
)
or via JOIN
SELECT DISTINCT c1.*
FROM flag_counts c1
LEFT JOIN flag_counts c2
ON c1.content_id = c2.content_id and c2.flag_id = 1
WHERE c2.content_id IS NULL;
This addresses the question: How to change my query so that it returns all content_ids that were not flagged with "1"? The "clarification" is actually more confusing, because SQL tables represent unordered sets, so there is no "last" row.
If you have a separate table of content ids, then the fastest approach might be:
select c.*
from contents c
where not exists (select 1
from flag_count fc
where fc.content_id = c.content_id and flag_id = 1
);
For this, you would want an index on flag_count(content_id, flag_id).
If you use flag_count instead of contents, then you might get duplicates.
One of the following method can be used to solve this.
SELECT DISTINCT a.content_id
FROM
flag_counts a
LEFT JOIN flag_counts b
ON (a.content_id = b.content_id AND b.flag_id = 1)
WHERE b.content_id IS NULL;
SELECT DISTINCT a.content_id FROM flag_counts a
WHERE NOT EXISTS (SELECT 1 FROM flag_counts b WHERE a.content_id = b.content_id AND b.flag_id = 1)
Fiddle Example 1
Fiddle Example 2
Can anyone please tell me if it's possible to combine the following two result sets into one in order not to use two separate queries?
PRODUCT_PAGE_ID PRODUCT_PAGE_NAME SIMILAR_PRODUCT SIMILAR_PRODUCT_ID RESULT
22 Nokia 8234 E821 777 2
22 Nokia 8234 HTC 2811 444 2
PRODUCT_PAGE_ID PRODUCT_PAGE_NAME VOTER_NAME COMMENT
22 Nokia 8234 John blahblahblah
22 Nokia 8234 David xxxxxxxxxxxx
22 Nokia 8234 Peter yyyyyyyyyyyy
22 Nokia 8234 John zzzzzzzzzzzz
My expected outcome should be:
PRODUCT_PAGE_ID PRODUCT_PAGE_NAME SIMILAR_PRODUCT SIMILAR_PRODUCT_ID RESULT VOTER_NAME COMMENT
22 Nokia 8234 E821 777 2 NULL NULL
22 Nokia 8234 HTC 2811 444 2 NULL NULL
22 Nokia 8234 NULL NULL NULL John blahblahblah
22 Nokia 8234 NULL NULL NULL David xxxxxxxxxxxx
22 Nokia 8234 NULL NULL NULL Peter yyyyyyyyyyyy
22 Nokia 8234 NULL NULL NULL John zzzzzzzzzzzz
The first result set uses this query,
SELECT p.product_page_id,p.name AS product_page,
p2.name AS similar_product,
p2.product_page_id AS similar_product_id,COUNT(poll.choice) AS result
FROM poll
INNER JOIN product p ON poll.product_page_id = p.product_page_id
INNER JOIN product p2 ON poll.choice = p2.product_page_id
WHERE poll.product_page_id = 22
GROUP BY poll.choice
ORDER BY result DESC
While the second one uses
SELECT pc.product_page_id,p.name AS product_page_name,
u.name AS voter_name,pc.comment
FROM `poll_comment` pc INNER JOIN `user` u
ON u.user_id = pc.user_id
INNER JOIN `product` p ON pc.product_page_id = p.product_page_id
WHERE pc.product_page_id = 22
LIMIT 10;
I have a poll that allows users to vote for any suggested products similar to the product they are browsing. They are allowed to vote multiple items at a time and leave a comment. Each voted item uses a single row inserted into table poll. I'm trying to pull the vote counts as well as 10 comments from the voters in one single query. The problem is that the query that I have come up with is not able to get the correct vote counts or the comments on the product.
Table Schema:
CREATE TABLE poll
(`user_id` int,`product_page_id`int,`choice` int)
;
INSERT INTO poll
(`user_id`,`product_page_id`,`choice`)
VALUES
(1,22,444),
(1,22,777),
(2,22,444),
(3,22,777)
;
CREATE TABLE poll_comment
(`user_id` int,`product_page_id`int,`comment` varchar(40))
;
INSERT INTO poll_comment
(`user_id`,`product_page_id`,`comment`)
VALUES
(1,22,'blahblahblah'),
(2,22,'xxxxxxxxxxxx'),
(3,22,'yyyyyyyyyyyy'),
(1,33,'zzzzzzzzzzzz'),
(2,33,'kkkkkkkkkkkk')
;
CREATE TABLE user
(`user_id` int, `name` varchar(30))
;
INSERT INTO user
(`user_id`, `name`)
VALUES
(1,'John'),
(2,'David'),
(3,'Peter'),
(4,'May')
;
CREATE TABLE product
(`product_page_id` int, `name` varchar(30))
;
INSERT INTO product
(`product_page_id`, `name`)
VALUES
(1,'Sony A821'),
(22,'Nokia 8234'),
(444,'HTC 2811'),
(777,'E821')
;
Here's my attempt (Fiddle):
SELECT * FROM (
SELECT p.name AS product_page,poll.product_page_id,p2.name AS similar_product,
COUNT(poll.choice) As vote_result
FROM `poll`
INNER JOIN product p ON poll.product_page_id = p.product_page_id
INNER JOIN `product` p2 ON poll.choice = p2.product_page_id
GROUP BY poll.choice
ORDER By vote_result desc
)TAB1
JOIN
(
SELECT pc.comment,pc.product_page_id,u.name
FROM `poll_comment` pc
INNER JOIN `product` p ON pc.product_page_id = p.product_page_id
INNER JOIN `user` u ON u.user_id = pc.user_id
LIMIT 10
)TAB2
ON TAB1.product_page_id = TAB2.product_page_id
WHERE TAB1.product_page_id = 22
Try the following approach by using result of two querys and union all:
select PRODUCT_PAGE_ID,PRODUCT_PAGE_NAME,SIMILAR_PRODUCT,SIMILAR_PRODUCT_ID,RESULT,null as VOTER_NAME , null as COMMENT from query1
UNION ALL
select PRODUCT_PAGE_ID,PRODUCT_PAGE_NAME,null as SIMILAR_PRODUCT,null as SIMILAR_PRODUCT_ID,null as RESULT,VOTER_NAME,COMMENT from query2
Exact answer based on your data:
select PRODUCT_PAGE_ID,product_page as PRODUCT_PAGE_NAME,SIMILAR_PRODUCT,SIMILAR_PRODUCT_ID,RESULT,null as VOTER_NAME , null as COMMENT from (SELECT p.product_page_id,p.name AS product_page,
p2.name AS similar_product,
p2.product_page_id AS similar_product_id,COUNT(poll.choice) AS result
FROM poll
INNER JOIN product p ON poll.product_page_id = p.product_page_id
INNER JOIN product p2 ON poll.choice = p2.product_page_id
WHERE poll.product_page_id = 22
GROUP BY poll.choice
ORDER BY result DESC)temp
UNION ALL
select PRODUCT_PAGE_ID,PRODUCT_PAGE_NAME,null as SIMILAR_PRODUCT,null as SIMILAR_PRODUCT_ID,null as RESULT,VOTER_NAME,COMMENT from (SELECT pc.product_page_id,p.name AS product_page_name,
u.name AS voter_name,pc.comment
FROM `poll_comment` pc INNER JOIN `user` u
ON u.user_id = pc.user_id
INNER JOIN `product` p ON pc.product_page_id = p.product_page_id
WHERE pc.product_page_id = 22)TEMP2
Hi i have 3 tables as follow:
business:
business_id business_name cat_id sub_cat_id
1 bz1 1 1001
2 bz2 1 1005
3 bz3 2 2001
4 bz4 1 1001
business_category:
cat_id cat_name
1 Restaurant
2 Food
3 Travel
business_sub_category:
b_sub_cat_id b_subcat_name b_maincat_id
1001 Italian 1
1002 French 1
1003 Asian 1
2001 Bagels 2
Now my query seems not work well when i want to find business based on b_subcat_name using like. This are my query:
SELECT `b`.`business_name`, `b`.`cat_id`, `b`.`business_id`, `bc`.`cat_name`, `bsc`.`b_subcat_name`, `bsc`.`b_sub_cat_id` FROM `business` AS `b` INNER JOIN `business_category` AS `bc` ON b.cat_id = bc.cat_id INNER JOIN `business_sub_category` AS `bsc` ON b.sub_cat_id = bsc.b_sub_cat_id WHERE (bc.cat_name like 'italian%') OR (bc.cat_name like '%italian') OR (bc.cat_name = 'italian')
Thanks!
I think you should using b_subcat_name in WHERE clause, because no cat_name has value contains Italy
Try to change this:
SELECT `b`.`business_name`, `b`.`cat_id`, `b`.`business_id`, `bc`.`cat_name`, `bsc`.`b_subcat_name`, `bsc`.`b_sub_cat_id` FROM `business` AS `b` INNER JOIN `business_category` AS `bc` ON b.cat_id = bc.cat_id INNER JOIN `business_sub_category` AS `bsc` ON b.sub_cat_id = bsc.b_sub_cat_id WHERE (bc.cat_name like 'italian%') OR (bc.cat_name like '%italian') OR (bc.cat_name = 'italian')
to
SELECT `b`.`business_name`, `b`.`cat_id`, `b`.`business_id`, `bc`.`cat_name`, `bsc`.`b_subcat_name`, `bsc`.`b_sub_cat_id` FROM `business` AS `b` INNER JOIN `business_category` AS `bc` ON b.cat_id = bc.cat_id INNER JOIN `business_sub_category` AS `bsc` ON b.sub_cat_id = bsc.b_sub_cat_id WHERE (bsc.b_subcat_name like 'italian%') OR (bsc.b_subcat_name like '%italian') OR (bsc.b_subcat_name = 'italian')
I am having one table users in which I have one field 'id' and another field is 'parent id'. Also I have expected target field in the users table.
I am having list of users till the 8th level hierarchy. Where A is parent of B and B is parent of C and so on.
e.g
A level 0
|
B level 1
|
c level 2
Now when I am looking for user A. I want to get the all the sub users using sql query 'expected target'.
i.e. When I use id = id of A then I can see the expected target of A,B,C etc.
If expected_targets for A, B and C are 1000, 500 , 200 respectively the output should be like :
id parent_id expected_target
A_id 1000
B_id A_id 500
C_id B_id 200
this will do the job - http://sqlfiddle.com/#!2/0de1f/7:
select u1.id, u1.parent_id, u1.expected_target
from users u1
left join users u2 on u1.parent_id = u2.id
left join users u3 on u2.parent_id = u3.id
left join users u4 on u3.parent_id = u4.id
left join users u5 on u4.parent_id = u5.id
left join users u6 on u5.parent_id = u6.id
left join users u7 on u6.parent_id = u7.id
left join users u8 on u7.parent_id = u8.id
where :A_id in (u1.id, u2.id, u3.id, u4.id, u5.id,
u6.id, u7.id, u8.id, u8.parent_id)
SET search_path='tmp';
DROP TABLE targets CASCADE;
CREATE TABLE targets
( id integer not null primary key
, parent_id integer references targets(id)
, expected_target integer
);
INSERT INTO targets(id,parent_id,expected_target) VALUES
(1,NULL, 1000), (2,1, 500), (3,2, 200);
WITH RECURSIVE zzz AS (
SELECT t0.id, t0.parent_id
, 0::integer AS level
, t0.expected_target
FROM targets t0
WHERE t0.parent_id IS NULL
UNION
SELECT t1.id, t1.parent_id
, 1+zzz.level AS level
, t1.expected_target
FROM targets t1
JOIN zzz ON zzz.id = t1.parent_id
)
SELECT * FROM zzz
;
OUTPUT:
SET
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "targets_pkey" for table "targets"
CREATE TABLE
INSERT 0 3
id | parent_id | level | expected_target
----+-----------+-------+-----------------
1 | | 0 | 1000
2 | 1 | 1 | 500
3 | 2 | 2 | 200
(3 rows)
UPDATE: if you don't want the whole tree, the true tree and nothing but the tree but only a subtree part of it, you can of course change the conditions a bit:
WITH RECURSIVE zzz AS (
SELECT t0.id, t0.parent_id
, 0::integer AS level
, t0.expected_target
FROM targets t0
-- WHERE t0.parent_id IS NULL
WHERE t0.id = 2
UNION
SELECT t1.id, t1.parent_id
, 1+zzz.level AS level
, t1.expected_target
FROM targets t1
JOIN zzz ON zzz.id = t1.parent_id
)
SELECT * FROM zzz
;
As this is tagged with PostgreSQL:
with recursive users_tree as (
select id,
parent_id,
expected_target,
1 as level
from users
where id = 'A_id'
union all
select c.id,
c.parent_id,
c.expected_target,
p.level + 1
from users c
join users_tree p on c.parent_id = p.id
)
select *
from users_tree
MySQL is not advanced enough to support this. There you'll need to do a self-join for each level.