MySQL conditional select when a inner join involves - mysql

I am working on a project where we develop a tourist app. Some items are add to the system as
advertisements (Hotels, Night clubs) and some items add as useful information (beaches,national parks)
I have a table called items.
items
id | category_id | name | is_payment
1 | 1 | hotel califonia | 1
2 | 1 | hotel hilton | 1
3 | 2 | Yala national park | 0
4 | 2 | Kumana national park| 0
here category_id refers the categories table.
categories
id | name | type
1 | Hotels | commercial
2 | National parks | non-commercial
I want to select all the items from the item table.
But if it's a commercial item I need to check whether payment is made or not before display to the end users.
I don't have to show anything what I did up-to now.
I though the CASE function and IF function as well. But I don't know how to link those with this case study.
I have no idea how to do this.

I below query you can I am taking all non-commercial item Or if it is commercial then I am checking payment is also done using and operator.
SELECT i.*
FROM items i
JOIN categories c
ON i.category_id = c.id
WHERE c.type != 'commercial'
OR (c.type = 'commercial' AND i.is_payment = 1)

If you are looking for another query other than the above answer...
select a.* from items a join categories b on a.category_id=b.id and b.type='commercial' and a.is_payment=1
union all
select a.* from items a join categories b on a.category_id=b.id and b.type<>'commercial';

Related

Sql left outer join with three tables

I am developing basically an e-commerce application. Application has two pages (all product and my-basket) authenticated user can add product to own basket. and I have three tables, the tables contains following data. I want to if the user adds product to own basket, these products don't exist on this user's all product page.
How should be the SQL query? I am looking query for all product page. so query's return type must be Product.
If user added any products to own basket on all product page these products
shouldn't see on the all product page for this user.
PRODUCT TABLE
+-------+--------+
| id | name |
+-------+--------+
| 1 | p1 |
| 2 | p2 |
+-------+--------+
USER TABLE
+-------+--------+
| id | name |
+-------+--------+
| 3 | U1 |
| 4 | U2 |
+-------+--------+
BASKET TABLE
+-------+---------+-------------+
| id | fk_user | fk_product |
+-------+---------+-------------+
| 5 | 3 | 1 |
| 6 | 4 | 2 |
+-------+---------+-------------+
So if authenticated user's id is 3. The user should see p2 product on own all product page.
try this:
SELECT product.name
FROM product
LEFT JOIN basket ON basket.fk_product = product.id
WHERE (basket.fk_user != 3 OR basket.fk_user IS NULL)
Check my demo query
If you want you can also join the user table but with the data you gave me is not necessary.
A left join keeps all rows in the first (product) table plus all rows in the second (basket) table, when the on clause evaluates to true.
When the on clause evaluates to false or NULL, the left join still keeps all rows in the first table with NULL values for the second table.
or, more commonly...
SELECT p.name
FROM product p
LEFT JOIN basket b
on b.fk_product = p.id
AND b.fk_user = 3
WHERE b.fk_user is null
What you are describing sounds like NOT EXISTS:
SELECT p.name
FROM product p
WHERE NOT EXISTS (SELECT 1
FROM basket b
WHERE b.fk_product = f.id AND
b.fk_user = 3
);
This seems like the most direct interpretation of your question.

Many To Many join with additional where

I think I have a somewhat trivial question but I can't figure out how this works. I have the following Companies and Products tables with a simple Many-To-Many relationship.
How would I have to extend this query, so that the results just contains let's say all companies which have products with id 1 AND 2?
I tried adding wheres and havings wherever I could imagine but all i could get was all companies which have products with id x (without the additional and)
Companies Table
id | name
-----------------
1 | Company 1
2 | Company 2
3 | Company 3
Companies_Products Table
id | product_id | company_id
----------------------------
1 | 1 | 1
2 | 2 | 1
3 | 3 | 1
4 | 1 | 2
5 | 1 | 3
6 | 2 | 3
Products Table
id | name
-----------------
1 | Product A
2 | Product B
3 | Product C
Statement
SELECT companies.name,
companies.id AS company_id,
products.id AS product_id
FROM companies
LEFT JOIN company_products
ON companies.id = company_products.company_id
INNER JOIN products
ON company_products.product_id = products.id
If you want ALL companies with associated products 1 and 2, you can write this query:
SELECT c.name,
c.id AS company_id
FROM companies c
WHERE (SELECT COUNT(*)
FROM company_products cp
WHERE cp.company_id = c.id
AND cp.product_id in ('1', '2')
) = 2
Go to Sql Fiddle
If you want to know informations about associated product in the main query so you must use a join in addition of existing query.
Maybe you could using the following subquery in your query:
SELECT company_id, count(*) as no_companies
FROM Companies_Products
WHERE product_id IN (1, 2)
HAVING count(*) = 2
(In this case company an product must be coupled only once.) It returns all the company_ids with product 1 and 2.
There always some discussion about subquery's and performance, but I don't think you will notice.
You could make this function flexible by using a array.
pseudo code:
$parameter = array(1, 2);
...
WHERE product_id IN $parameter
HAVING count(*) = count($parameter)
Please say so if you need more help.

MySQL selective GROUP BY, using the maximal value

I have the following (simplified) three tables:
user_reservations:
id | user_id |
1 | 3 |
1 | 3 |
user_kar:
id | user_id | szak_id |
1 | 3 | 1 |
2 | 3 | 2 |
szak:
id | name |
1 | A |
2 | B |
Now I would like to count the reservations of the user by the 'szak' name, but I want to have every user counted only for one szak. In this case, user_id has 2 'szak', and if I write a query something like:
SELECT sz.name, COUNT(*) FROM user_reservations r
LEFT JOIN user_kar k ON k.user_id = r.user_id
LEFT JOIN szak s ON r.szak_id = r.id
It will return two rows:
A | 2 |
B | 2 |
However I want to every reservation counted to only one szak (lets say the highest id only). I tried MAX(k.id) with HAVING, but seems uneffective.
I would like to know if there is a supported method for that in MySQL, or should I first pick all the user ID-s on the backend site first, check their maximum kar.user_id, and then count only with those, removing them from the id list, when the given szak is counted, and then build the data back together on the backend side?
Thanks for the help - I was googling around for like 2 hours, but so far, I found no solution, so maybe you could help me.
Something like this?
SELECT sz.name,
Count(*)
FROM (SELECT r.user_id,
Ifnull(Max(k.szak_id), -1) AS max_szak_id
FROM user_reservations r
LEFT OUTER JOIN user_kar k
ON k.user_id = r.user_id
GROUP BY r.user_id) t
LEFT OUTER JOIN szak sz
ON sz.id = t.max_szak_id
GROUP BY sz.name;

Using SQL to return results which match ALL the items in an array using joined tables

I'm stumped on a SQL query and need your help. In plain English, I need to return all of the hotels which offer packages that contain ALL the items I select.
Tables involved are:
hotels (hotel_id, hotel_name)
packages (package_id, hotel_id, package_name)
items (item_id, package_id, item_name)
package_items (package_id, item_id)
SAMPLE DATA
To help explain my problem, let's look at this sample data to illustrate it more clearly.
Hotels table
----------------------
hotel_id | hotel_name
----------------------
1 | Hilton
2 | Westin
----------------------
Packages table
-----------------------------------------
package_id | hotel_id | package_name
-----------------------------------------
1 | 1 | Gold Package
2 | 1 | Silver Package
3 | 2 | Star Package
4 | 2 | Ultimate Package
-----------------------------------------
Items table
-------------------------------
item_id | item_name
-------------------------------
1 | Free Room Upgrade
2 | Dinner Included
3 | Spa Access
4 | Complimentary Papers
-------------------------------
Package Items table
---------------------
package_id | item_id
---------------------
1 | 2
1 | 3
1 | 4
2 | 2
2 | 3
3 | 4
4 | 1
4 | 2
4 | 3
4 | 4
---------------------
So, using this data you can see that for example, the Hilton's Gold Package contains Dinner Included, Spa Access, and Complimentary Papers.
I'm building a feature on our frontend that lets the user filter for these various package items. A check box against each item, which when clicked should filter the results to show only the hotels which offer packages that contains ALL of the items selected by the user.
I'm a little stumped on the SQL for this. So far my research has brought me to the following sample code (found here: PostgreSQL where all in array):
SELECT conversation_id FROM conversations_users
WHERE user_id IN (1,2)
GROUP BY conversation_id HAVING COUNT(*) = 2
In this sample query the key part of the query is the HAVING COUNT() = 2 section. With this, I expect to force the query to ensure that ALL selected items must be matched, not just one, which is the default behavior of the query without the HAVING COUNT() = 2 section.
Using this approach, and the above table structures I have adapted this code to suit our own requirement, including the various JOIN queries etc. In the below example I've selected Dinner Included, Spa Access, and Complimentary Papers. I expect to get back both the Hilton and the Westin as both of these hotels offer packages which include ALL of these items. In the case of the Hilton their Gold Package offers what I've selected, and in the case of the Westin their Ultimate Package offers what I've selected.
Remember, I'm trying to find hotels which offer packages that contain ALL the items I'm searching for.
Here's what I've got so far:
SELECT h.hotel_id, h.hotel_name FROM hotels h
JOIN packages p ON h.hotel_id = p.hotel_id
JOIN package_items pi ON p.package_id = pi.package_id
JOIN items i ON pi.item_id = i.item_id
WHERE i.item_id IN (2,3,4)
GROUP BY h.hotel_id HAVING COUNT (*) = 3;
Unfortunately I'm not getting accurate results here and I'm stumped as to how to fix it. Serious kudos to anyone who can solve this problem for me.
Update your last query as:
SELECT h.hotel_id, h.hotel_name FROM hotels h
JOIN packages p ON h.hotel_id = p.hotel_id
JOIN package_items pi ON p.package_id = pi.package_id
JOIN items i ON pi.item_id = i.item_id
WHERE i.item_id IN (2,3,4)
GROUP BY h.hotel_id
HAVING COUNT (DISTINCT i.item_id) = 3;
This will return you the hotels having 3 different items in the offer.
Add the package_name to the grouping... as follows, have it in the initial SELECT to show what it is working off
SELECT h.hotel_id, h.hotel_name, p.package_name FROM hotels h
JOIN packages p ON h.hotel_id = p.hotel_id
JOIN package_items pi ON p.package_id = pi.package_id
JOIN items i ON pi.item_id = i.item_id
WHERE i.item_id IN (2,3,4)
GROUP BY h.hotel_name, p.package_name HAVING COUNT(*) = 3;
+----------+------------+------------------+
| hotel_id | hotel_name | package_name |
+----------+------------+------------------+
| 1 | Hilton | Gold Package |
| 2 | Weston | Ultimate Package |
+----------+------------+------------------+

mysql query for items with multiple conditions (item options)

What I like to achieve is
a: display all Items that are in all of the selected category's
b: return / update the category list with category's available based on selection
I like items to be stored and be found by use of the adjacency list model or nested sets.
I've experimented with both and may use advice what would be the best for this case.
Currently I'm using (testing with) the adjacency list model like this:
items:
ID | item_name
====================
1 | car
2 | boat
3 | bike
items_cats: (many to many)
iid | cid
====================
1 | 1
1 | 2
1 | 4
1 | 7
2 | 1
2 | 3
2 | 4
2 | 7
3 | 1
3 | 3
3 | 4
3 | 8
categorys:
ID | cat_name | parent_id
========================
1 | safety: | 0 (0 = no parent)
2 | safe | 1
3 | dangerous | 1
4 | fun: | 0
5 | a bit | 5
6 | boring | 5
7 | funny | 5
8 | cool | 5
So its no problem to get items based on cid but how would you:
1st: selection:
1- Display all items who have cat id: cid 7 (funny)?
2- return (array/object) of all category's who have items that also contain cid 7?
Would you all do this in one query or would two be more efficient?
2nd: selection:
3- Display all items who have cat id: cid 7 and also contain cat id '3' (dangerous)
4- return (array/object) of all category's who have items that contain cid 7 and cid 3?
For selecting on multiple category's I found the flowing solution. Is this a good one and would there be to gain any performance especially when the number of category's grow?
SELECT
DISTINCT t1.product_id, t1.category_id
FROM
items_cats t1
INNER JOIN
items_cats t1b
ON t1.iid =t1b.iid
WHERE
t1.cid=3 AND
t1b.cid=7
To get a list of all items that have category ID = 7, start with your many:many table
select
i.item_name
from
items_cat ic
join items i
on ic.iid = i.id
where
ic.cid = 7
to get all categories associated with any item that has the category ID of 7, you can expand from the first and get categories associate for those item IDs
select DISTINCT
ic2.cid,
c.cat_name,
coalesce( CatParent.cat_name, "" ) as ParentCategoryName
from
( select distinct ic.iid
from items_cat ic
where ic.cid = 7 ) QualifiedItems
JOIN items_cat ic2
on QualifiedItems.iid = ic2.iid
JOIN categorys c
on ic2.cid = c.id
LEFT JOIN categorys CatParent
on c.parent_id = CatParent.ID
For 3 and 4, it would be similar, but to qualify BOTH (or anytime, more than one), you need to apply an OR, a GROUP BY and make sure that the final count matches those you were trying to qualify
select
i.item_name
from
items_cat ic
join items i
on ic.iid = i.id
where
ic.cid in( 3, 7 )
group by
i.item_name
having
count(*) = 2
So you can better understand and apply these principles, I'll leave the last one for you to try and implement... If you really get stuck, let me know... :)