Mutually exclusive values in SQL - mysql

I have a query which selects products from a table. A product can have multiple prices (think of various prices) and a default price.
Naturally, this is a one-to-many relation. I need to select the products which have a given price, or the default price - which means mutual exclusion. I know this can be done through separate queries and a WHERE (not) IN clauses or a union statement, but I'm convinced a more optimal way must be possible. My query currently looks like this:
SELECT products.*, products_prices.price
FROM products RIGHT JOIN
products_prices ON (products.id = products_prices.productId)
WHERE products_prices.businessId = ?
OR products_prices.businessId IS NULL // this needs to become mutual.
EDIT: I ended up using this query, which is a slightly modified version of Gordon Linoff's:
SELECT distinct p.*, coalesce(pp.price, defpp.price)
FROM products p LEFT JOIN
products_prices pp
ON p.id = pp.productId and pp.businessId = ? left join
products_prices defpp
on p.id = defpp.productId and defpp.businessId is NULL

If I understand your question correctly, the products table would have the default price and the product_prices table would have any other price.
You want to know where the default price is being used, meaning that there are no other prices. For this, use a left outer join:
SELECT p.*, coalesce(pp.price, p.default_price)
FROM products p LEFT OUTER JOIN
products_prices pp
ON p.id = pp.productId
WHERE pp.price = GIVENPRICE or pp.price is null
Based on your comment, you are storing the default prices in records with the business id being NULL. In this case, I would do two joins to the prices table:
SELECT p.*, coalesce(pp.price, defpp.price)
FROM products p LEFT OUTER JOIN
products_prices pp
ON p.id = pp.productId and pp.price = GIVENPRICE left outer join
products_prices defpp
on p.id = defpp.productId and defpp.businessId is NULL
The first join gets the price matching the given price. The second gets the default price. The first result is used, if present, otherwise the second is used.

Related

Mysql count left join strange result

Can someone help me to understand those results ? (For me all 3 should return 6455).
(Using RDS mysql-8.0.13)
SELECT COUNT(p.product_id) FROM product p LEFT JOIN product_attributes pa ON p.pdt_id = pa.pdt_id WHERE pa.code = 'season';
Results : 6332
SELECT COUNT(*) FROM product p;
Results : 6455
SELECT COUNT(p.product_id) FROM product p LEFT JOIN product_attributes pa ON p.pdt_id = pa.pdt_id AND pa.code = 'season';
Results : 6455
Your first join uses the WHERE clause, this mean sit selected all the rows, including those with a null join and then filters out those WHERE the pa.code = season, i.e. the null joins.
The last one joins on both, but because it is a left join you still get the full table of results, and nothing is filtered because you remove the WHERE clause. If you were to use an INNER JOIN in the last query you should get the same result (6332).
This link might be useful What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?

Mysql (doctrine) - count inner join having count > X

I have SQL to count products with specific properties. I am using it in the products filter. SQL is very long, but here is the primary part:
SELECT COUNT(products.id) as products_count, property_items.description, property_items.id as id
FROM property_items
INNER JOIN product_properties ON property_items.id = product_properties.property_item_id
INNER JOIN products ON product_properties.product_id
INNER JOIN product_properties pp ON products.id = pp.product_id AND (pp.property_item_id IN ($ids))
GROUP BY property_items.id
HAVING COUNT(pp.id) >= $countIds
This works perfectly when I have only the one element in $ids, but when i choose one more, the result is bad. It looks like the sql returns count of all products with any property from $ids, but I need to count only products that contains all properties.
First get all available properties. On each property join products that contains this property and go back to all properties of this product to check, if product contains already checked properties too. Or it is bad idea? I need to keep primary table (FROM table) as property_items.
I need to get result in this format:
=============================
id|description|products_count
=============================
1 |lorem ipsum|10
-----------------------------
2 |dolore sit |2
Thanks for any idea.
Try to use SELECT COUNT (DISTINCT products.id) as cnt
You can get the product ids that have all the properties by doing:
SELECT pp.property_id
FROM property_items pi INNER JOIN
product_properties pp
ON pi.id = pp.property_item_id INNER JOIN
products p
ON pp.product_id = p.id
WHERE pp.property_item_id IN ($ids)
GROUP BY pp.property_id
HAVING COUNT(DISTINCT pp.property_item_id) = $countIds -- has all of them
Note that I rationalized the joins. I think your simplification of the query wasn't quite right. I also added table aliases, so the query is easier to write and to read.
If you want the count of such products, use a subquery:
SELECT COUNT(*)
FROM (SELECT pp.property_id
FROM property_items pi INNER JOIN
product_properties pp
ON pi.id = pp.property_item_id INNER JOIN
products p
ON pp.product_id = p.id
WHERE find_in_set(pp.property_item_id, $ids)
GROUP BY pp.property_id
HAVING COUNT(DISTINCT pp.property_item_id) = $countIds -- has all of them
) ;
Your problem is probably because of this line:
WHERE pp.property_item_id IN ($ids)
If you are passing $ids as a comma-separated string, then your query will not work. Note the replacement above.

MySQL JOIN tables with COUNT values

I have the following tables in my database.I only listed the important columns which can be used for joining.
I need to get the following output
Currently I'm using two seperate queries for each COUNT value
For assigned licenses
select
products.id,products.name,COUNT(assigned_licenses.id)
from
deployment_users
inner join
assigned_licenses
on
deployment_users.id = assigned_licenses.deployment_user_id
inner join
products
on
assigned_licenses.id = products.id
and
deployment_users.customer_id = 10
group by
assigned_licenses.id
;
For total licenses
select
products.id,products.name,COUNT(total_licenses.id)
from
customers
inner join
total_licenses
on
customers.iccode = licenses.iccode
inner join
products
on
total_licenses.id = products.id
and
customers.id = 10
group by
total_licenses.id
;
Since there are more than a 1,000 products that need to be listed,I want to combine them into a single query.How can I do that?
Your specification leaves some room for interpretation (e.g. can a user have assigned licenses without total licenses? if yes my query will fail.) but I would go with this.
SELECT
products.id,
products.name,
Count(Distinct total_licenses.id) As CountTotalLicenses,
Count(Distinct assigned_liceses.deployment_users_id) As CountAssignedLicenses
FROM products
LEFT JOIN total_licenses ON total_licenses.products_id = products.id
LEFT JOIN customers ON customers.iccode = total_licenses.customers_iccode
LEFT JOIN assigned_licenses ON assigned_liceses.total_licenses_id = total_licenses.id
WHERE
customers.id = 10
GROUP BY
products.id,
products.name
For the future it would be awesome if you could paste code as code and not as an image. People cannot simple copy paste snippets of your code and have to type everything again...
Try joining Both of your query
SELECT * FROM (
(First Query) as assigned_licn
INNER JOIN
(Second Query) as total_licn
USING (id)
);

Inner Join for list of records NOT in a second table (NOT Inner Join)

I have a table let's call it products with a list of Manufacturers and Products.
I have a second table let's call it Customer, Orders.
I can do a join to make a list of all the items from each manufacturer the customer ordered doing an Inner Join. Yet trying to do an Inner Join for the items they did not fails.
I tried an Inner Join with 'Orders.Product != Products.Product' but that only works where the Customer has one order. Once there is more than one order I get the same list I would have doing an Inner Join. Any thoughts? I'll try to make a SqlFiddle tonight but was hoping a quick description might help a MySql / Join expert who has done 'NOT Inner Join'before...
It is called an anti join, you can use left join with is null check:
select p.*
from products p
left join orders o on p.Product = o.Product
where o.product is null

Joining table with conditions

I have a problem with tables joining.
I have two tables: product_commons and product_prices. Product_prices has records with standard product prices + pre-generated prices for campaigns (campaign_id). I need a query which returns me list of products with campaign prices (if exists) or standard prices (if they not).
Something like this:
SELECT * FROM product_commons
INNER JOIN product_prices ON product_commons.id = product_prices.product_id
WHERE (campaign_id = 3 OR campaign_id IS NULL)
GROUP BY product_commons.id
Unfortunnely this query returns me prices only with campaign_id = NULL.
You need to use a mix of INNER and LEFT (OUTER) JOINs with a COALESCE, with the campaign_id filter in the JOIN conditions. To get 2 prices from different rows into one row, you need 2 JOINs.
SELECT
*,
COALESEC(pstd.price, pc.price)
FROM
product_commons pc
JOIN
product_prices pstd ON pc.id = pstd.product_id AND pstd.campaign_id IS NULL;
LEFT JOIN
product_prices pp ON pc.id = pp.product_id AND pp.campaign_id = 3;
You don't need a GROUP BY either: it doesn't make sense (and would give errors used like this) in standard SQL or other RDBMS
Since you are looking up different rows of prices for campaign prices and default prices, you need two joins: one for the default price (inner, because the default is always there) and one for the campaign #3 price (outer, because it may not exist). COALESCE returns its first operand if it is not null; otherwise, it returns its second operand.
SELECT *,COALESCE(c3_price.price,def_price.price)
FROM product_commons
LEFT OUTER JOIN product_prices c3_price ON product_commons.id = c3_price.product_id and c3_price.campaign_id = 3
INNER JOIN product_prices def_price ON product_commons.id = def_price.product_id and def_price.campaign_id IS NULL
use a left join
Select * FROM product_commons LEFT JOIN product_prices ON product_commons.id = product_prices.product_id WHERE (campaign_id = 3 OR campaign_id IS NULL)