ORDER BY on multiple conditions - mysql

I need a query that will give me the result that is either (a) the highest price or (b) the one with the oldest timestamp. In all cases, price should trump timestamp (ie if a record has a really old timestamp by higher price than all others, it should always return the record with the highest price)
Here are a few scenarios:
id | price | date
1 | 5 | 2012-02-20 08:59:06
2 | 5 | 2012-02-20 09:59:06
3 | 7 | 2012-02-20 10:59:06
Should return id 3 because it is highest price
id | price | date
1 | 5 | 2012-02-20 08:59:06
2 | 5 | 2012-02-20 09:59:06
3 | 5 | 2012-02-20 10:59:06
should return id 1 since it is the oldest
In my current query i am doing this:
SELECT * FROM table ORDER BY price, date DESC LIMIT 1
Unfortunately this query is not working how i've outlined it above.
Thanks for your help

I'm having trouble determining precisely what you are after, however it sounds like you are looking for the oldest timestamp for the highest price, and so the following should suffice
SELECT *
FROM table
ORDER BY
price DESC, // Favour the highest price
date ASC // Now get the one with oldest date at this price
LIMIT 1

i hope i understand your question, try this
SELECT
p.*
FROM table p
WHERE price = (SELECT MAX(price) FROM table)
OR date = (SELECT MIN(date) FROM table)

Related

select query performs wrong results on parallel execution

Following is my scenario
I have tables named
Products
id | name | count | Price
-------------------------
1 | meat | 1 | 10
Users
id | name | balance
-----------------
1 | Tim | 10
2 | Joe | 10
Work flow
select products if count >= 1,
reduce user's balance and count = count - 1
if no_balance or count < 1 throw error
Let's say if both users placing an order for 1 product at exact same time, products table count updates to -1, means query executes for both users.
Products
id | name | count | Price
-------------------------
1 | meat | -1 | 10
During placeing of an order,I have used the below query to select matching products
Select * from products where count >= 1 and price >= 10
Also, if users place orders with even little time difference, the expecting output gathered.
Is there any solution to this ?
You should consider use lock for each row, for example.
Select * from products where count >= 1 and price >= 10 FOR UPDATE.
But in your scenario, I advice you use Redis to do that.
How to design a second kill system for online shop

How to write a sql show the quantity result based on filter and merge two different column if no filtering option?

Table
|---------------------|------------------|------------------|
| ID | qty_ordered | qty_received |
|---------------------|------------------|------------------|
| 10 | 30 | |
|---------------------|------------------|------------------|
| 10 | 30 | 20 |
|---------------------|------------------|------------------|
I have a filter to let user choose whether to show qty_ordered or qty_received. When user select filter for qty_ordered then result will be 30 else if select qty_received then result will be 20 else will return 50.
SQL
select sum(qty_ordered) as order, sum(qty_received) as received from table...
Currently i will select both, and using if-else statement in my html to show the result of quantity. If filtering by qty_ordered and qty_received will be fine, but problem occurred when need to display all quantity. Mean, i need to merge ordered quantity and received quantity with same id as one row.
|---------------------|------------------|
| ID | quantity |
|---------------------|------------------|
| 10 | 50 |
|---------------------|------------------|
ps: Ordered quantity will only be sum up if there is no received quantity.
You can try aggregating by the ID and using SUM to obtain single values for the quantity ordered and received:
SELECT
ID,
SUM(qty_ordered) AS qty_ordered,
SUM(qty_received) AS qty_received,
SUM(COALESCE(qty_received, qty_ordered)) AS qty_total
FROM yourTable
GROUP BY ID;
ID qty_ordered qty_received qty_total
1 10 60 20 50
I use a COALESCE trick in the expression for the total sum. The logic is that if the quantity received is present, we count it, even if the quantity ordered might also be present. If the quantity received be absent, only then do we count the quantity ordered.
Demo

Select dates between date range and sum over a column

I have the following tables:
Let's assume the first table represents a booking made by a customer for a hotel.
+------------+-----------+-----------+--------------+----------------+
| booking_id | client_id | room_type | arrival_date | departure_date |
+------------+-----------+-----------+--------------+----------------+
| 1 | 1 | 1 | 2016-05-30 | 2016-06-03 |
+------------+-----------+-----------+--------------+----------------+
The second table contains the prices for different types of room. The prices vary for different periods of time.
+---------------+-----------+------------+------------+-------+
| room_price_id | room_type | start_date | end_date | price |
+---------------+-----------+------------+------------+-------+
| 1 | 1 | 2016-03-01 | 2016-05-31 | 150 |
| 2 | 1 | 2016-06-01 | 2016-08-31 | 200 |
+---------------+-----------+------------+------------+-------+
My question is: how can I calculate the total cost for a booking if the price is not the same during the whole period of the booking? (in my case the total cost would be 700)
I read the following links: MySQL: Select all dates between date range and get table data matching dates and Select data from date range between two dates, but I don't get it on how to solve my problem since the entries in the table with the prices contain only date ranges and not explicit dates as in the first link.
The trick with your question is in formulating the join condition between the booking and prices tables. Once this is out of the way, a simple aggregation gets you the result you want. The join condition should be true whenever any point in a given price range falls within the range of the arrival or departure date.
Then, to count days we use the greater of the arrival date or the start date, and the lesser of the departure date or the end date.
SELECT
b.booking_id,
SUM((DATEDIFF(LEAST(b.departure_date, p.end_date),
GREATEST(b.arrival_date, p.start_date)) + 1) * p.price) AS total
FROM booking b
INNER JOIN prices p
ON b.room_type = p.room_type AND
(p.start_date BETWEEN b.arrival_date AND b.departure_date OR
p.end_date BETWEEN b.arrival_date AND b.departure_date)
GROUP BY b.booking_id;
The output for your sample data is 900. This is correct because:
2 days x 150 = 300
3 days x 200 = 600
total = 900
Demo here:
SQLFiddle
And here is another demo which highlights the case where a stay overlaps an entire price range.

Get one record per unique column value

I have a table that list system licences, multiple licences for each system (the expired ones and existing ones). I've only posted two columns in this question as they're the only important ones.
| id | systemid |
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 3 |
| 6 | 3 |
I need to get the rows with the id of 2, 4 and 6.
I need to collect 1 record for each systemid and it has to be the earliest (youngest) record, so in this case, the record with the highest id. I've been exploring GROUP BY, ORDER BY and LIMIT but I'm not producing the result I'm after. How do you collect one record for each individual value in one column and make sure it's the record with the highest id?
I KNOW this is wrong, but it's what I'm currently starring at:
SELECT * FROM licences GROUP BY systemid ORDER BY id DESC LIMIT 1
SELECT max(id), systemid FROM table GROUP BY systemid
Note that with a GROUP BY, all columns you select must either be in the GROUP BY clause or wrapped in an aggregating function, like max, min, sum, or average.
This will grab the highest id per systemid.
SELECT MAX(id), systemid
FROM ...
GROUP BY systemid

Apply WHERE after GROUP BY

I have a table with items that are part of a thread, so they have a thread ID, each item also has a date (UNIX timestamp). My table looks something like the following (UNIX timestamps simplified):
+-----------------------------+
| id | date | thread_id |
+-----+---------+-------------+
| 1 | 1111 | 4 |
| 2 | 1333 | 4 |
| 3 | 1444 | 5 |
| 4 | 1666 | 5 |
+-----------------------------+
What I want to do is select thread IDs where ALL the items sharing the same thread ID are smaller than the passed date. So if I wanted thread IDs where ALL items are older than 1555 (date < 1555), I would only expect to have thread ID 4 returned, not 5 even though it's got an item with a date smaller than 1555. This is what I tried:
SELECT * FROM table WHERE date < 1555 GROUP BY thread_id ORDER BY date DESC
What I'm trying to do with that query is group all items by thread ID around the item with the highest date value and from there get the items where the date is less than 1555. But that doesn't work, it will still return thread ID 5 because it's got an item older than 1555.
So to summarize, how would I only select thread IDs where ALL items are older than a certain date?
SELECT thread_id FROM table GROUP BY thread_id HAVING MAX(date) < 1555
Use a HAVING clause. This allows you to filter on aggregates like SUM, MAX... Here you want to select only those thread id's whose newest entry is older than 1555, so you write:
SELECT * FROM table
GROUP BY thread_id
HAVING MAX(date) < 1555
ORDER BY date DESC
You can use having in place of where:
select customer_id,sum(amount),count(amount) from payment group by customer_id
having sum(amount)> 100