Joining table with conditions - mysql

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)

Related

Selecting only last record inside the JOIN statement

I have the following query:
SELECT
usp.user_id AS userId,
usp.create_time AS create_time,
ml.amount AS amount
FROM user_subscription_plan AS usp
RIGHT JOIN product AS product ON product.id = usp.product_id
LEFT JOIN modification_log AS ml ON ml.subscription_id = usp.id
WHERE usp.id IN ('447482')
I have three tables, from which I need to select data.
My problem begins with the last LEFT join.
modification_log table could have no entries, but also it could have more entries.
I want to select only the latest entry. With the above query, if I have 2 (or more) entries in the modification_log, I receive 2 identical results (repeated).
What I would like to get:
If there are no results in the modification_log, then it will return null. I think that is covered with LEFT JOIN. But also, in the case of many record, I would need to select the latest added one (amount)
I believe I might need a sub-query, but I fail to implement it.
You have to use a subquery for taking left join with modification_log table as
SELECT
usp.user_id AS userId,
usp.create_time AS create_time,
ml.amount AS amount
FROM user_subscription_plan AS usp
RIGHT JOIN product AS product ON product.id = usp.product_id
LEFT JOIN
(select * modification_log where subscription_id
IN ('447482') order by created_at desc LIMIT 1)
AS ml ON ml.subscription_id = usp.id
WHERE usp.id IN ('447482')
Note that the where clause in subquery select * modification_log where subscription_id IN ('447482')
is the same as with the last where condition
Just add a max condition after your left join to get the latest entry to be joined, like below-
LEFT JOIN modification_log AS ml ON ml.subscription_id = usp.id
where usp.id IN ('447482') and ml.id = (select max(id) from modification_log)

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)
);

If row exist in a table then use another tables value in SQL

What i would like to archieve:
Getting the correct sum of the total amount of the orders that has been cancelled of user id 2002.
Some pre information:
I am having deals which that has its price in deals.price and its id in deals.ID
I then have orders with a foreign key to deals.ID
Running this SQL:
select SUM(deals.price), orders.* from orders
JOIN deals ON deals.ID = orders.deal_id
where orders.user_id = 2002
and orders.cancelled = 1
Works just fine.
Here is where i get stuck:
As an addition to deals, each deals has products with their own prices.
Table is called deal_products, deal_products.price hold the price and deal_products.product_id has the ID of it.
A order is attached to a deal product in another table called order_products, where order_products.product_id = deal_products.product_id
To sum up: I would like to do is including a if inside the above SQL.
If a order has a row in order_products, get the order_products.product_id and find the price in deal_products (price) and use this instead of deals.price when SUM()'ing.
If there is no row it should use deals.price.
How can this be archieved? To first look in another table if there is a entry, and then further look in to a third table and get a value to use?
You can use COALESCE + LEFT JOIN:
select SUM(coalesce(dp.price, d.price)), o.*
from orders o JOIN deals d ON d.ID = o.deal_id
LEFT JOIN order_products op on op.order_id = o.id
LEFT JOIN deal_products dp on op.product_id = dp.product_id
where o.user_id = 2002 and o.cancelled = 1
group by ...;
COALESCE function returns first not null operand
LEFT [OUTER] JOIN = [INNER] JOIN + all rows of the structure on the left side of the LEFT JOIN keyword, which don't match the ON clause in the right structure.

Mutually exclusive values in SQL

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.