MYSQL query for one to many relation - mysql

i have two tables:-
1- section
id name location_id
1 demo1 20
2 demo2 34
2- amenities
id amenity_id amenity_type object_id object_type
1 wedding_hall venue_type 1 section
2 conference_hall venue_type 1 section
3 conference_hall venue_type 2 section
i want all those section whose location_id is 134 and who has wedding_hall and conference hall both. I have tried this query:-
SELECT s.* from section s
INNER JOIN amenities am
on (am.object_type='section' AND am.object_id=s.id)
WHERE s.location_id=134 AND
(am.amenity_type LIKE 'venue_type' and am.amenity_id='wedding_hall')
AND (am.amenity_type LIKE 'venue_type' and am.amenity_id='conference_hall')
if i do this query:-
SELECT s.* from section s
INNER JOIN amenities am
on (am.object_type='section' AND am.object_id=s.id)
WHERE s.location_id=134 AND
(am.amenity_type LIKE 'venue_type' and am.amenity_id='wedding_hall')
then it works bot not for more than one amenity.
here is sqlfiddle
how can i correct my query?

I think you need subqueries for that. Maybe take a look at the EXISTS condition. The following will give you the desired result if the section has both wedding hall and conference hall (but not if you have just two wedding halls, and it's working with more than one section):
SELECT DISTINCT s.* FROM section s
INNER JOIN amenities a
ON a.object_id = s.location_id
WHERE s.location_id = 134
AND EXISTS(SELECT * FROM amenities WHERE object_id = s.location_id AND amenity_id = 'wedding_hall')
AND EXISTS(SELECT * FROM amenities WHERE object_id = s.location_id AND amenity_id = 'conference_hall');
Maybe add amenity_type and object_type conditions to the WHERE clauses.

If I did not mistake your question and you really want section which id = 1, try this:
SELECT s.*
FROM section s
INNER JOIN amenities am
ON am.object_type='section' AND am.object_id=s.id
WHERE s.location_id=134
AND am.amenity_type LIKE 'venue_type'
AND am.amenity_id IN ('wedding_hall', 'conference_hall')
-- HAVING COUNT(DISTINCT am.amenity_id) > 1
GROUP BY s.id
HAVING COUNT(DISTINCT am.amenity_id) = 2
SQLFiddle Demo

Related

Select ID of a row with max value

How can I select the ID of a row with the max value of another column in a query that joins multiple tables?
For example, say I have three tables. tblAccount which stores a grouping of users, like a family. tblUser which stores the users, each tied to a record from tblAccount. And each user can be part of a plan, stored in tblPlans. Each plan has a Rank column that determines it's sorting when comparing the levels of plans. For example, Lite is lower than Premium. So the idea is that each user can have a separate plan, like Premium, Basic, Lite etc..., but the parent account does not have a plan.
How can I determine the highest plan in the account with a single query?
tblAccount
PKID
Name
1
Adams Family
2
Cool Family
tblUsers
PKID
Name
AccountID
PlanID
1
Bob
1
3
2
Phil
2
2
3
Suzie
2
1
tblPlans
PKID
Name
Rank
1
Premium
3
2
Basic
2
3
Elite
4
4
Lite
1
Here's the result I'm hoping to produce:
AccountID
Name
HighestPlanID
PlanName
2
Adams Family
1
Premium
I've tried:
SELECT U.AccountID, A.Name, MAX(P.Rank) AS Rank, P.PKID as HighestPlanID, P.Name as PlanName
FROM tblPlans P
INNER JOIN tblUsers U ON U.PlanID = P.PKID
INNER JOIN tblAccounts A ON U.AccountID = A.PKID
WHERE U.AccountID = 2
and the query will not always work, selecting the MAX of Rank does not select entire row's values from tblPlans.
I am looking for a solution that is compatible with mysql-5.6.10
You can join the tables and use ROW_NUMBER() to identify the row you want. Then filtering is ieasy.
For example:
select *
from (
select a.*, p.*,
row_number() over(partition by a.pkid order by p.rank desc) as rn
from tblaccount a
join tblusers u on u.accountid = a.pkid
join tblplans p on p.pkid = u.planid
) x
where rn = 1
Inside the subquery you can add where u.accountid = 2 to retrieve a single account of interest, instead of all of them.
With the help of #the-impaler, I massaged their answer a bit and came out with something very similar:
select *
from (
select a.*, p.*
from tblaccount a
join tblusers u on u.accountid = a.pkid
join tblplans p on p.pkid = u.planid
where u.accountid = 2
order by p.rank desc
) x limit 1
The subquery sorts each user by plan rank from top to bottom, and then the top level query selects the top most row with limit 1. It seems to work!

Remove duplicates based on rank after join in SQL request

I am using MySQL 5.6.
I have a SQL table with a list of users:
id name
1 Alice
2 Bob
3 John
and a SQL table with the list of gifts for each user (numbered in order of preference):
id gift rank
1 balloon 2
1 shoes 1
1 seeds 3
1  video-game 1
2 computer 2
3 shoes 2
3 hat 1
And I would like a list of the preferred gift for each user (the highest rank - if two gifts have the same rank, pick only one randomly) (bonus: if the list could be randomized, that would be perfect!):
id name gift rank
2 Bob computer 2
1 Alice shoes 1
3 John hat 1
I tried to use the clause GROUP BY but without any success.
Considering rank as a part of your data; Without using window functions or complex sub queries
SELECT u.id, u.name, g.gift
FROM users u
JOIN gifts g ON g.id = u.id
LEFT JOIN gifts g2 ON g2.id = g.id AND g2.rank > g.rank
WHERE g2.id IS NULL;
Added link http://sqlfiddle.com/#!9/62f59e/15/0
You can use row_number to get one row for each User.(Mysql 8.0+)
SELECT A.ID,NAME,GIFT,`RANK` FROM USERS A
LEFT JOIN (
SELECT ID,GIFT,`RANK` FROM
(SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY `RANK` ASC) AS RN FROM X) X
WHERE RN =1
) B
ON A.ID= B.ID
I do not know DB what you use. And I'm not an expert in SQL(I can have some mistake in next). But I think it is not difficult.
So I can give you just advice that you have to think gradually. Let me write.
First All I need is the highest rank. So I have to get this.
SELECT MAX(RANK)
FROM GIFT
GROUP BY ID
And then I think that I need get gifts from this rank.
SELECT GIFT.*
FROM GIFT
INNER JOIN(
SELECT ID, MAX(RANK)
FROM GIFT
GROUP BY ID
) filter ON GIFT.ID = filter.ID AND GIFT.RANK = filter.RANK
I think this is the table what you want!
So If below code works, That's what you really want.
SELECT *
FROM USER
LEFT OUTER JOIN(
above table
) GIFT ON USER.ID = GIFT.ID
But Remember this, I said I'm not an expert in SQL. There can be better way.
Checkout the query
SELECT tbluser.id,name,gift,rank into tblrslt
FROM tbluser
LEFT JOIN tblgifts
ON tbluser.id = tblgifts.id order by id,rank;
SELECT tt.*
FROM tblrslt tt
INNER JOIN
(SELECT id, min(rank) AS rank
FROM tblrslt
GROUP BY id) groupedtt
ON tt.id = groupedtt.id
AND tt.rank = groupedtt.rank order by id
In MySQL versions older than 8 you have no ranking functions available. You'll select the minimum rank per user instead and use these ranks to select the gift rows. This means you access the gifts table twice.
I suggest this:
select *
fron users u
join gifts g
on g.id = u.id
and (g.id, g.rank) in (select id, min(rank) from gifts group by id)
order by u.id;
If you also want to show users without gifts, simply change the inner join to a left outer join.

Is it possible to Show Text instead of ID using SELECT * from TableName

I have two tables as given below:
person_detail
id name role_id
---------------------------
1 Hamdan 1
2 Sara 2
3 John 2
tbl_user_roles
id role
---------------
1 Admin
2 Author
Result I need:
id name user_role
---------------------------
1 Hamdan Admin
2 Sara Author
3 John Author
Note: is it possible? to use on [Select * from tblPerson] statement, because I do not want to type each field name like:
I have these Two(2) solutions, but I want to use SELECT statement only.
Solution 1
SELECT p.id, p.person_name, roles.role
FROM person_detail p
INNER join user_roles roles
ON p.role_id = roles.id
Solution 2
SELECT p.id,
p.person_name,
(SELECT user_roles.role FROM dbo.user_roles WHERE id = p.role_id) As 'user_role'
FROM dbo.person_detail p
::Both works fine, but I don't want ti use above two solutions
Here is another solution without JOIN keyword
Example 1:
SELECT
*
FROM
`person_detail`,
`tbl_user_roles`
WHERE
person_detail.role_id = tbl_user_roles.id
Note : Above query will return all columns from both tables
Example 2:
SELECT
person_detail.id,person_detail.name,tbl_user_roles.role AS user_role
FROM
`person_detail`,
`tbl_user_roles`
WHERE
person_detail.role_id = tbl_user_roles.id
Note : Above query will return selected columns from both tables
Important Note : But you shouldn't use this type of query. You should Use JOIN keyword when manipulate data with multiple tables
You could use the following, but using the asterix is a bad pratice, unless it's for testing purposes.
SELECT p.*, roles.role
FROM person_detail p
INNER join user_roles roles
ON p.role_id = roles.id
You can create a view of what you want to see:
CREATE VIEW view_user_roles AS
SELECT p.id, p.person_name, roles.role
FROM person_detail p
INNER join user_roles roles
ON p.role_id = roles.id
and then select from the view:
select * from view_user_roles

Matching items in one table that don't match in a subset of a second table

Suppose I have a Product table, and a
id product
1 Apple
2 Bag
3 Cat
4 Ducati
and a Cart table
id user_id product_id
1 1 2
2 1 3
3 2 1
4 3 1
So, I want to look at a particular user and see what he/she does NOT have in their Cart.
In other words, in the above example
SELECT ...... WHERE user_id=1 .....
would return Apple and Ducati because User 1 already has Bag and Cat.
(This may well duplicate another question but there are so many variations I couldn't find the exact match and put in these simple terms may help)
Perform a left join from product to all products purchased by user1, which can be retrieved with a subselect in the join. This will cause all product id's that are not in user1's care to have null product ids. The where clause will select all null product id's meaning they will not have been in a users cart, essentially filtering purchased items.
select p.name
from product p
left join (select product_id, user_id
from cart where user_id = 1)
c
on p.id = c.product_id
where c.product_id is null;
SQL Fiddle: http://sqlfiddle.com/#!2/5318eb/17
Select
*
From Product p
Where p.id Not In
(
Select c.product_id
From Cart c
Where User ID = ____
)
SELECT product FROM product_table
WHERE product NOT IN
(SELECT product_id FROM cart_table WHERE user_id = 1);
This will give you all product for all users which are not in there cart.
select c.user_id,a.Product
from cart c Cross Join product a
left Join
cart b on b.product_id=a.id and c.user_id=b.user_Id
where b.product_id is null
group by c.user_id,a.Product
Sql Fiddle Demo

A Better way to optimize these MySQL queries

I have two MySQL tables:
attributes (attributeid, name)
productsattributes (productid, attributeid, displayvalue)
The required is for each attribute name called "Product Type" get all other attributes associated with this "Product Type". As an example — attributes table will look like:
attributeid name
1 A
2 B
3 Product Type
4 D
productsattributes table will look like:
productid attributeid displayvalue
1 3 FAN
1 1 Brown
1 2 Stand
2 3 FAN
2 4 D
3 3 CAR
3 4 imported
So the final result should be:
FAN (A,B, Product Type,D)
CAR (Product Type, imported)
Here is my try:
first I get all the "displayvalues" from productattributes:
SELECT DISTINCT displayvalue
FROM productsttributes
WHERE attributeid = 3;
then I loop through each "displayvalues" to find the other attributes:
SELECT a.name
FROM attributes a
INNER JOIN productsattributes pa
ON pa.attributeid = a.attributeid AND productid in (
SELECT productid
FROM productsttributes
WHERE dispalyvale = '$displayvalue')
ORDER BY a.name;
The problem is the productattributes table has about 7 million rows, so my script is taking forever .. of course I am not looking for 10 minutes solution but at least it will improve my queries a bit.
I would start with the following statements:
ALTER TABLE attributes ADD CONSTRAINT p_attributes PRIMARY KEY (attributeid);
ALTER TABLE productsattributes ADD CONSTRAINT p_productsattributes
PRIMARY KEY(productid, attributeid);
ANALYZE TABLE attributes, productsattributes;
This will make sure all important fields are indexed.
The query might look like this (also on SQL Fiddle):
SELECT trg.displayvalue,
group_concat(a.name ORDER BY trg.productid, a.attributeid)
FROM (
SELECT t.productid,t.displayvalue
FROM attributes a
JOIN productsattributes t USING (attributeid)
WHERE a.name = 'Product Type') AS trg
JOIN productsattributes p ON p.productid = trg.productid
JOIN attributes a ON a.attributeid = p.attributeid
GROUP BY trg.displayvalue
ORDER BY 1;
Please, kindly include the EXPLAIN output of your's and this queries into your question.
Try this ::
Select displayvalue, attribute_name
from
(Select
product_id from productsattributes pa inner join attributes_table at on (pa.attributeid=at.id) where at.name=?) as productList
inner join productsattributes pa2 on(pa2.product_id=productList.product_id)
inner join attributes_table at2 on (pa2.attributeid=at2.id)