MySQL [When joining an empty table] - mysql

Can anyone tell me how to replace empty fields.
For example:
I have 2 tables.
If there is no information on the data in the sample, the word should be displayed as a result: No
This script displays empty fields, that is, there are no "orders" in the database with such conditions.
select o.id as order_id , o.status
from `order` o
left join order_status os on o.id = os.order_id
where o.status = 14
GROUP by o.id
HAVING COUNT(os.status = 14)=1
order by rand()
limit 1
I tried to use IF but it didn't work.

Related

select DISTINCT email based off of matching ID across tables

I'm trying to pull unique emails that have matching IDs across two tables.
SELECT line_items.order_id, line_items.id, orders.email, orders.name
FROM orders INNER JOIN
line_items
ON orders.id = line_items.order_id
WHERE line_items.order_id IN (SELECT DISTINCT email FROM orders WHERE status = 0 AND created_at BETWEEN '2018-01-10' AND NOW() )
LIMIT 50;
I know my error is based upon the fact that the line_items.order_is is an INT and therefore the IN parameter is looking for another int column to match against. However, I'm not sure how to modify this to get pull the proper results. Any and all help is greatly appreciated.
I'm trying to pull unique emails that have matching IDs across two tables.
If you mean distinct emails, then your subquery would appear to do this:
SELECT DISTINCT o.email
FROM o.orders
WHERE o.status = 0 AND o.created_at BETWEEN '2018-01-10' AND NOW();
Because an order should have at least one line item, I don't see why that table is necessary.
Your query comes close to answering the question: "Pull all orders for emails that have made a recent order with status 0". If that is what you want:
SELECT li.order_id, li.id, o.email, o.name
FROM orders o INNER JOIN
line_items li
ON o.id = li.order_id
WHERE o.email IN (SELECT o2.email FROM orders o2 WHERE o2.status = 0 AND o2.created_at BETWEEN '2018-01-10' AND NOW() )
LIMIT 50;
Hard to follow but I think you want:
SELECT sub.order_id, sub.line_item_id, sub.email, o.name
FROM
(SELECT o.email, MIN(o.id) AS order_id, MIN(i.id) AS line_item_id
FROM orders o
INNER JOIN line_items i
ON o.id = i.order_id
WHERE o.status = 0
AND o.created_at BETWEEN '2018-01-10' AND NOW()
GROUP BY o.email) sub
LEFT JOIN orders o
ON sub.order_id = o.id
In the sub-query, select each email along with the first order ID and line item ID. Then join this back to orders to pull the order name. This does assume that the MIN(line_item) will show up with the MIN(order_id) for each email, so you'll have to let me know if that is not a valid assumption.

MYSQL Subquery and count number of rows containing a value

I have two tables oc_order o and oc_order_total ot
oc_order o has fields o.customer_id, o.date_added, o.email,o.total
And oc_order_total ot has fields ot.code and ot.value
I want to show results only if customer orders more than 3 times that is if customer_id repeats thrice or more in the result and show ot.value where ot.code = 'shipping'
I am trying to do following
SELECT COUNT(o.customer_id) AS 'Orders Count', o.date_added, o.email,o.total, ot.value
FROM oc_order o
Inner join oc_order_total ot ON ot.order_id = o.order_id
WHERE count(o.customer_id) > 3 AND ot.value = (select value from oc_order_total where code = 'shipping' )
GROUP BY o.customer_id
I am getting Invalid use of group error and I think I am not using subquery correctly in where clause.
You can not use SUM/COUNT in WHERE statement. You need to use for this HAVING operator. Try this query:
SELECT COUNT(o.customer_id) AS 'Orders Count', o.date_added, o.email,o.total, ot.value
FROM oc_order o
INNER JOIN oc_order_total ot ON ot.order_id = o.order_id AND
WHERE ot.code IN (select value from oc_order_total where code = 'shipping' )
GROUP BY o.customer_id
HAVING count(o.customer_id) > 3
EDIT: Adding example with ot.value not affected by GROUP BY.
SELECT o.order_id, q.orders_count AS 'Orders Count', o.date_added, o.email,o.total, ot.value
FROM oc_order o
INNER JOIN oc_order_total ot ON ot.order_id = o.order_id
INNER JOIN (SELECT o.customer_id, COUNT(o.customer_id) AS orders_count
FROM oc_order o
INNER JOIN oc_order_total ot ON ot.order_id = o.order_id
WHERE ot.code IN (select value from oc_order_total where code = 'shipping' )
GROUP BY o.customer_id
HAVING count(o.customer_id) > 3) AS q ON q.customer_id = o.customer_id
Basically what happens here is that you pre-filter customers with previous query and then use these pre-filtered list to get individual orders for these customers meeting your criteria. On this individual orders_id you can perform any operation without grouping cause you already eliminated customers not meeting your needs. Hope it helps.
NO, you are getting error cause your GROUP BY doesn't contains all the column listed in SELECT list and you can't use COUNT() aggregate function like that in WHERE condition; it's only allowed in HAVING clause. You can modify your query like
SELECT o.date_added
, o.email
, o.total
, ot.value
FROM oc_order o
JOIN oc_order_total ot
ON ot.order_id = o.order_id
JOIN
( SELECT COUNT(customer_id) AS 'Orders Count'
, customer_id
FROM oc_order
GROUP
BY customer_id
) xxx
ON xxx .customer_id = o.customer_id
WHERE xxx.`Orders Count` > 3
AND ot.code = 'shipping';
For one thing, aggregates cannot be referenced in the WHERE clause.
The predicates in the WHERE clause are evaluated when the rows are accessed. At the time the rows are being retrieved, MySQL doesn't have any information about the value returned by aggregate functions (e.g. COUNT()).
The COUNT() aggregate will be evaluated after the rows are accessed, and after the GROUP BY operation.
An aggregate can be referenced in a HAVING clause, following the GROUP BY clause.
GROUP BY ...
HAVING COUNT(...) > 3
Note that the HAVING clause is evaluated after the GROUP BY operation, and after the values of the aggregate expressions are evaluated. Much different than the WHERE clause.
Also, the subquery is a bit odd. Because it's being referenced in an equality comparison, the subquery can return at most one row. We don't see anything in the query that would prevent that, unless code is guaranteed to be unique in `ot_.
If code is unique, then we wouldn't need a subquery at all, we could just test for code.
WHERE ot.code = 'shipping'
If there are multiple rows with "shipping" in the code column, we don't see any guarantee that the value column on those rows will be the same. To test for any of the possible values, we could use an IN operator instead of the equality (scalar) comparison. e.g.
WHERE ot.value IN ( SELECT v.value
FROM oc_order_total v
WHERE v.code = 'shipping'
)
But that still looks really odd. What's strange is that it's using the same table as the outer query. If we are using a subquery to lookup the set of values related to a string code, that's usually a separate lookup table. And we'd normally prefer a JOIN operation rather than an IN (subquery). Very strange.
Also, the non-aggregate expressions in the SELECT list
o.date_added, o.email, o.total, ot.value
Do not appear in the GROUP BY clause. Most relational databases will throw an error "non-aggregate in select not in group by" something of that ilk.
But a MySQL extension will allow the query to run, but the values returned for the non-aggregates is indeterminate. MySQL will return values for those expressions based on some row included in the collapsed set, but there's no guarantee which row that will be.
We can also get MySQL to throw an error like other databases, by including ONLY_FULL_GROUP_BY in the sql_mode variable.

Filtering orders having one item and not having another one in the same time

I have very simple one-to-many relation (tables Orders-Items) where one order can have zero or more items.
What's the best way (SQL query) to filter only orders having item1 (item_id=1) and no item2 (item_id=2) in the same time?
At the moment I use the following SQL query:
SELECT o.order_id
FROM orders o
JOIN items i ON i.order_id=o.order_id
WHERE i.item_id=1
AND o.order_id NOT IN (SELECT o2.order_id
FROM orders o2
JOIN items i2 ON i2.order_id=o2.order_id
WHERE i2.item_id=2)
I prefer to approach these types of questions using group by and having:
SELECT i.order_id
FROM items i
GROUP BY i.order_id
HAVING SUM(i.item_id = 1) > 0 AND
SUM(i.item_id = 2) = 0;
Some notes:
You don't need to join in orders, because you have order_id in items.
Each condition in the having clause is counting the number of items. The first says there is at least one of item 1 and the second that there is no item 2.
I removed the where clause. If you were to have one, then it would be WHERE i.item_id IN (1, 2).
EDIT:
In any database other than MySQL, you would use this HAVING clause:
HAVING SUM(CASE WHEN i.item_id = 1 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN i.item_id = 2 THEN 1 ELSE 0 END) = 0;
This will work in MySQL as well; I just like the shorter notation.
No need for a correlated subquery, use the HAVING clause.
This excludes all order_ids where an item_id = 2 exists.
SELECT o.order_id
FROM orders o
JOIN items i ON i.order_id=o.order_id
WHERE i.item_id=1
GROUP BY o.order_id
HAVING SUM(item_id = 2) = 0;
Or you could do
SELECT o.order_id
FROM orders o
JOIN items i ON i.order_id=o.order_id
WHERE i.item_id=1
GROUP BY o.order_id
HAVING COUNT(DISTINCT item_id) = 1;
to make sure it has only item_id = 1. Several ways to do it, depending on what you want to do exactly.

Issue with sql query to get the results

I am having a problem with a sql query. This is how the table structure looks like.
The scenario is there are order details in order table(Initially shipping details id is null) and ordered items are also saved accordingly. When dispatching the orders the driver can select one or more orders. Assume if he selects two orders('CC0-C1B-50B-63B' and 'FB2-FC6-57B-DD8') a new record is added to the shipping_details table(so the no of places is 2 in our case) and two records are saved accordingly in Delivery_Place table.
What I want : If I select order No 'CC0-C1B-50B-63B', I want to get all the delivery orders(In our case there is another order which is 'FB2-FC6-57B-DD8'and the result should be those 2 orders) and the departmentID in orderd_items table for those 2 orders. (If there are multiple items in a single order, all items are in a same department)
So I tried this query,
select oi.FKOrderID,dp.deliveryPlaceID,dp.FKShippingDetailsID,dp.city,dp.position,oi.FKDepartmentID
from
`order` o join `shipping_details` sd
on o.FKShippingDetailsID = sd.shippingDetailsID
join `ordered_items` oi
on o.orderID = oi.FKOrderID
join `delivery_places` dp
on dp.FKShippingDetailsID=sd.shippingDetailsID
where o.orderID = 'CC0-C1B-50B-63B'
group by dp.deliveryPlaceID
order by dp.position asc ;
And the result is this,
But I for second row I should get 'FB2-FC6-57B-DD8' as FKOrderID and '11' for the FKDepartmentID.
This is the data table
UPDATED WITH INPUT DATA
Order Table
Shipping Details Table
Ordered_items table (columns: orderedItemsID , FKItemID , quantity , size, FKDepartmentID , FKOrderID)
Department Table
So how to modify this query to get that result?
Thanks.
This is how I achieved the result ,
select
distinct(oi.FKOrderID),dp.deliveryPlaceID,dp.FKShippingDetailsID,dp.city,dp.longitude,dp.latitude,dp.timestamp,dp.position,dp.status,o.orderStatus,oi.FKDepartmentID
from
`ordered_items` oi,
`order` o join `shipping_details` sd
on o.FKShippingDetailsID = sd.shippingDetailsID
join `delivery_places` dp
on dp.FKShippingDetailsID=sd.shippingDetailsID
where o.orderID = 'CC0-C1B-50B-63B'
and
oi.FKOrderID in (select orderID from `order` where
FKShippingDetailsID=o.FKShippingDetailsID)
group by oi.FKOrderID
order by dp.position asc
And here is the result,

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.