I have troubles getting proper data.
I have table structure like:
id INT(11) AI
order_id INT(11)
status varchar(45)
This table log status changes for orders.
So order_id's will have few statuses.
Now I need to select rows and group them by order_id, where order never had status (not even one status with given order_id) != 'example'
We don't show orders, where one of members had status = example
Sample data
1 12 ready
1 12 example
2 13 ready
2 13 sent
So I don't want order 12 to show at all, because one of it members have "example" status
I've tried grouping results, but it's not enough.
you can do it by simple join query :
select a.order_id
from ordrstatus as a left outer join (select orderid , count(*) as status from orderstatus where status = 'example' group by orderid) as b on a.orderid = b.orderid
where b.status = 0 or b.status is NUll
Join query always run faster then IN query . by using Join in query it will run only one time .
You can try like this...it will return all order id which never had status -example
Select
Order_id,
from TableName A where Not Exists(
Select id from TableName B where
status='example' and
a.Order_id=b.Order_id
)
group by Order_id
Not quite sure if you want the records for order which have had a status of example, or ones which have never had a status of example
To get a list of orders (with the status grouped up) which have had a status of example:-
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a
INNER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b
ON a.order_id = b.order_id
GROUP BY order_id
To get those which have NEVER had a status of exmaple
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a
LEFT OUTER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b
ON a.order_id = b.order_id
WHERE b.order_id IS NULL
GROUP BY order_id
EDIT
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a -- Statuses
LEFT OUTER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b -- Get any order id which has had a status of example (as a LEFT JOIN)
ON a.order_id = b.order_id
INNER JOIN
(
SELECT order_id, MAX(id) AS Latestid
FROM SomeTable
GROUP BY order_id
) c -- Get the latest status for each order (ie, max id)
ON a.order_id = c.order_id
LEFT OUTER JOIN
(
SELECT order_id, id
FROM SomeTable
WHERE status = 'example2'
) d -- Get the id of the order status of example2
ON a.order_id = d.order_id AND c.Latestid = d.id -- join on the same order id and that the record id matches the latest record id
WHERE b.order_id IS NULL -- reject those where a match was found on example for any status
AND d.order_id IS NULL -- reject those where a match was found on example2 for the latest status
GROUP BY order_id
try this
SELECT Order_ID FROM tbl_Orders
WHERE Status NOT IN ('example')
GROUP BY Order_ID
SELECT DISTINCT x.order_id
FROM order_status x
LEFT
JOIN order_status y
ON y.order_id = x.order_id
AND y.status = 'example'
WHERE y.id IS NULL;
Related
How do you subtract (2)two different fields with the same data type of decimal(13,2) ?
I have table for users
attribtues:
id, name, password, email, deleted, created_at, updated
and
table for top_up_history
attributes:
id, userId, paymentId, paymentDesc, amount, deleted, created_at, updated_at
and
table for transaction_details
id, userId, merchId, transId, amount, refCode, procId, deleted, created_at, updated_at
and
I have view for user_balance
attributes:
userId, total_topup, total_balance
Here's my current query:
SELECT a.id as userId, SUM(b.amount) as total_topup,
SUM(b.amount) - SUM(c.amount) as total_balance
FROM `users` AS a LEFT JOIN `top_up_history` AS b
ON a.id = b.userId LEFT JOIN`transaction_details` as c
ON a.id = c.userId GROUP BY a.id
Now the current output of this user_balance is this:
But the problem is
the data from transaction_details is:
the data from top_up_history
the data from users
There's something wrong with my computation.
The output should be:
userId total_topup total_balance
1 NULL NULL
2 NULL NULL
3 15000 14,725
9 10150 9,875
you should manage the null value
SELECT a.id as userId
, SUM(ifnull(b.amount,0)) as total_topup
, SUM(ifnull(b.amount,0)) - SUM(ifnull(c.amount,0)) as total_balance
FROM `users` AS a
LEFT JOIN `top_up_history` AS b ON a.id = b.userId
LEFT JOIN`transaction_details` as c ON a.id = c.userId
GROUP BY a.id
Just take a look at resulting table without GROUP BY function.
LEFT JOIN top_up_history produces two rows for each unique user,
It caused by repeated userId values (9 and 3) in top_up_history table. At this moment (before joining transaction_details) you already have 6 rows. Now sql joins transaction_details and duplicates its amount column value for each matched row.
Then, finally you group, and sum duplicated values. Substruction itself works ok.
The easiest way to deal with that kind of problem is to do subquery like so:
SELECT a.id as userId, SUM(b.amount) as total_topup,
SUM(b.amount) -
(select sum(tr.amount) from transaction_details_ as tr where tr.users_id = a.id) as total_balance
FROM users_ AS a
LEFT JOIN top_up_history_ AS b ON a.id = b.users_id
LEFT JOIN transaction_details_ as c ON a.id = c.users_id
GROUP BY a.id
the table has 4 columns follows :
id int(11) not null primary key,
name varchar not null ,
status tinyint(1) not null,
created datetime not null
now, I want to find out the result rows with conditions :
the status is 0;
foreach the result rows as A, if the table can select a row name B that has the same name and created is the same day and status is 1, except the A.
could someone select the result with a sql without nested select, thanks a lot!
Sounds like an anti-join pattern fits the bill. This pattern requires two references to the table, but only a single SELECT keyword. As an example:
SELECT t.id
, t.name
, t.status
, t.created
FROM the_table t
-- anti-join exclude matching rows
LEFT
JOIN the_table d
ON d.name = t.name
AND d.status = 1
AND d.created >= DATE(t.created)
AND d.created < DATE(t.created) + INTERVAL 1 DAY
WHERE d.name IS NOT NULL
AND t.status = 0
The trick is the outer join, to return all rows from t, along with matching rows from d, with the condition in the WHERE clause that excludes all rows that had a match. Leaving only rows from t that didn't have a matching row from d.
name not in rows that has second condition:
SELECT * FROM tablename as o
WHERE o.status = 0
AND o.name NOT IN
(
SELECT `name` FROM tablename as i
WHERE i.status = 1
AND i.created
BETWEEN DATE(t.created)
AND DATE(t.created) + INTERVAL 1 DAY
)
Try this:
select a.id,a.name,a.created,a.status from
(select b.id,a.name,b.created,b.status from
(select count(name) as cnt, name from tbl_sample as a GROUP BY name) as a
LEFT JOIN
(select * from tbl_sample) as b
on a.`name` = b.`name` where cnt>1 and STATUS = 0) as a
LEFT JOIN
(select b.id,b.name,b.created,b.status from
(select count(created) as cnt, created from tbl_sample as a GROUP BY created) as a
LEFT JOIN
(select * from tbl_sample) as b
on a.`created` = b.`created` where cnt>1 and STATUS = 0) as b
on a.id = b.id
SELECT a.id, a.name, a.status, a.created
FROM userTable as a
LEFT JOIN userTable as b
ON a.name = b.name
AND b.status = 1
AND b.created BETWEEN DATE(a.created) AND DATE(DATE_ADD(a.created, INTERVAL 1 day))
WHERE a.status = 0
AND a.name IS NULL;
thanks the answer from #spencer7597 , I find out the result.
I try to get a list of products with each newest and lowest offer price
Table product:
id | name
Table offer:
id | product_id | price | created | dealer_id
Table invalids:
id | offer_id | status
I have tried:
SELECT * FROM product INNER JOIN
(
SELECT offer.product_id , offer.price
FROM offer
LEFT JOIN invalids
ON offer.id = invalids.offer_id
WHERE invalids.id IS NULL
GROUP BY offer.dealer_id
ORDER BY offer.created DESC
) o
ON o.product_id = product.id
ORDER BY product.name
I have tried an sqlfiddle http://sqlfiddle.com/#!9/32658/3 with this offer values:
(`id`, `price`, `dealer_id`, `product_id`, `created`)
(1,12.60,1,1,'2015-05-17 08:44:45'),
(2,13.00,1,1,'2015-08-17 08:44:45'),
(3,20.00,1,1,'2015-08-17 08:45:30'),
(4,10.00,1,1,'2015-08-17 08:45:46'),
(5,4.00,2,1,'2015-05-17 08:44:11'),
(6,11.00,2,1,'2015-08-17 08:44:46'),
(7,5.00,2,1,'2015-08-17 08:45:31'),
(9,110.00,2,2,'2015-08-17 08:46:58'),
(10,11.00,2,2,'2015-08-17 08:47:12');
Expected value for product ID 1 is offer ID 7 with price 5.
These steps I think I must realize:
Order offers by created and group by dealer_id to get newest entries
Take result from step 1 and order it by price to get smallest price.
Make this for all products
Maybe I must use a second SELECT FROM offer with GROUP BY and ORDER BY but how do I get I the product_id from the first (outer) select?
Well I would start by getting the latest date for each product offer like this:
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id;
Once you have that, you can join it to the original table to get that offer:
SELECT o.*
FROM offer o
JOIN(
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id) tmp ON tmp.product_id = o.product_id AND tmp.latestOffer = o.created;
Here is an SQL Fiddle example.
This query should help you:
SELECT *
FROM product
JOIN (
SELECT product_id, min(price) as minPrice, max(created) as newestOffer
FROM offer
WHERE id NOT IN (SELECT offer_id FROM invalids)
GROUP BY 1
) as b
ON product.id = b.product_id
A shot in the dark based on what I understand you to be after...
lots of nested subqueries.. keep thinking there's got to be a better way...
SELECT OO.ID, OO.Price, OO.Dealer_Id, OO.Product_ID, OO.created, P.name
FROM Offer OO
INNER JOIN (
SELECT Min(Price) as MinP
FROM offer O
INNER JOIN (
SELECT max(OI.created) as LatestOffer, OI.Dealer_ID, OI.Product_ID
FROM Offer OI
LEFT JOIN invalids I
on OI.Id = I.offer_Id
WHERE I.ID is null
GROUP BY OI.Dealer_Id, OI.Product_Id
) B
on O.Dealer_Id = B.Dealer_Id
and O.Product_Id = B.Product_Id
and O.Created = B.LatestOffer
) Z
on OO.Price = Z.MinP
INNER JOIN product P
on P.ID = OO.Product_ID
SQL FIDDLE
I have the following query which works perfectly, but only if each select finds a row.
I've attempted to add IFNULL to return 0 if no rows were found but I'm still not getting the correct return.
SELECT IFNULL(paid_value,0)-IFNULL(ordered_value,0)+IFNULL(credit_value,0) AS account_balance
FROM
(
SELECT customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
GROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS credit_value
FROM credits
WHERE customer_id = '1'
GROUP BY customer_id
) as credits
ON orders.customer_id = credits.customer_id
This query currently returns empty, it's not returning NULL or 0.
When I run
SELECT customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
It also returns empty, not NULL or 0, unless there's a row. In order for the full query to work, each of the 3 separate queries need to have a row in them.
Any ideas?
It if because none of the columns have a result set, so an empty result set is returned, if you want to always display a row in any case you can try with some tricks like this one for example :
SELECT IFNULL(SUM(payments.paid_value),0)-IFNULL(SUM(orders .ordered_value),0)+IFNULL(SUM(credits.credit_value),0) AS account_balance
FROM
(SELECT 1 AS idx, 0 AS paid_value, 0 AS ordered_value, 0 AS credit_value) a
LEFT JOIN
(
SELECT 1 AS idx, customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
ON a.idx = orders.idx
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
GROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS credit_value
FROM credits
WHERE customer_id = '1'
GROUP BY customer_id
) as credits
ON orders.customer_id = credits.customer_id
GROUP BY a.idx
The example is a proof of concept that can be adapted even to other situations where you need to always returns a row with default values even with no elements in underlying tables .
If you want row event if there is no data available, then I suppose you should either have UNION. Or In this scenario you can include you Customer Table & put each Table on Right Side, make a LEFT JOIN.
I think following select will give you an IDEA.
SELECT O.customer_id, SUM(O.order_total) AS ordered_value
FROM Customers C
left join orders O on C.ID =o.customer_id
WHERE O.customer_id = '1'
GROUP BY O.customer_id
I have an sql query that returns a list of residential units, and a subquery that is supposed to get the last entered bill for that unit.
However when I add LIMIT 1 to the subquery, no bill entries are returned? If I leave it out, I get duplicate unit rows depending on the number of bill for the unit.
select * from unit u
left join (select id as billId, unit_id, added_on, end_reading, bill_type from bills
order by id desc) b ON unit_id = u.id
where community_Id = 1
and unit_section = 7
and unit_floor in (1,2,3,4,5)
order by unit_floor, display_order asc;
Anyone know how I can the subquery result limited to 1 bill?
When using joins that duplicate your results, add a group by statement. It's an alternative of the distinct from a simple select
select * from unit u
left join (select id as billId, unit_id, added_on, end_reading, bill_type from bills
order by id desc) b ON unit_id = u.id
where community_Id = 1
and unit_section = 7
and unit_floor in (1,2,3,4,5)
group by u.id
order by unit_floor, display_order asc;
Think you will need a sub query to get the first (lowest) id for each unit_id from the bills table. Then use that to join between the unit and bills table, getting the other matching columns from bills for that lowest id
SELECT u.*, bills.*
FROM unit u
LEFT OUTER JOIN
(
SELECT unit_id, MIN(id) AS min_id
FROM bills
GROUP BY unit_id
) b ON b.unit_id = u.id
LEFT OUTER JOIN bills
ON b.unit_id = bills.unit_id
AND b.min_id = bills.id
WHERE u.community_Id = 1
AND u.unit_section = 7
AND u.unit_floor in (1,2,3,4,5)
ORDER BY u.unit_floor, u.display_order asc;