how to sort 2 different column in sql - mysql

can you guys show me how to sort user define column and fixed column name in sql. i need to display the highest transaction and outletid, instead i only get the highest transaction but the oulet id is not in grouping.
pardon me, im very bad at english
here is the problem
outlet id | revenue code | total transaction | total amount
6837 | 014 | 326 | 39158.94
6821 | 408 | 291 | 48786.50
6814 | 014 | 285 | 74159.76
6837 | 452 | 282 | 8846.80
and here is my sql
SELECT
outletid,
revcode,
count(receiptnumbe) as Transactions,
sum(amount) as total
FROM
user_payment
WHERE
date = (SELECT MAX(date) FROM user_payment GROUP BY date desc LIMIT 0, 1)
GROUP BY
outletid, revcode
ORDER BY Transactions desc
i need it to be like this. sort by outlet id and highest transactions.
outlet id | revenue code | total transaction | total amount
6837 | 014 | 326 | 39158.94
6837 | 452 | 282 | 8846.80
6821 | 408 | 291 | 48786.50
6814 | 014 | 285 | 74159.76

Is this what you want?
ORDER BY OutletId, Transactions desc
EDIT:
If I understand correctly, you want it sorted by the outlet that has the most total transactions. Then by transactions within that group. To do that, you need to summarize again at the outlet level and join back the results:
select outor.*
from (SELECT up.outletid, up.revcode, count(up.receiptnumbe) as Transactions,
sum(up.amount) as total
FROM user_payment up
WHERE date = (SELECT MAX(date) FROM user_payment)
GROUP BY outletid, revcode
) outor join
(SELECT up.outletid, count(up.receiptnumbe) as Transactions,
sum(up.amount) as total
FROM user_payment up
WHERE date = (SELECT MAX(date) FROM user_payment)
GROUP BY outletid
) o
on outor.outletid = o.outletid
order by o.Transactions desc, outor.outletid, outor.Transactions desc;

1)The first thing to do is make sure that you are sorting the fields the way you want to. Do you want them sorted numerically or alphabetically?
See Sorting Lexical and Numeric
Count should be numerical, but you should check outletid.
If you have access to the tables, you could change the field to a number type for it to be sorted numerically or a string for it to be sorted alphabetically.
You might have to use cast or convert. See Oracle Cast Documentation.
2)If you want the whole table sorted by outlet id and amount of transactions you might consider removing the group by clause.
3)The third thing I would look at even if this did work is renaming column names that had reserved words to the tables that were reserved words. I noticed transaction highlighted in blue.
When these things are checked Melon's comment should work.
Good question. Feel free to comment so I can follow up.

Related

SQL - return latest of multiple records from large data set

Background
I have a stock_price table that stores historical intra-day stock prices for roughly 1000 stocks. Although the old data is purged regularly, the table regularly has 5M+ records. Structure is loosely:
| id | stock_id | value | change | created_at |
|--------|----------|-------|--------|---------------------|
| 12345 | 1 | 50 | 2.12 | 2020-05-05 17:39:00 |
| 12346 | 2 | 25 | 1.23 | 2020-05-05 17:39:00 |
I regularly need to fetch the latest stock prices for ~20ish stocks at time for an API endpoint. An original implementation of this executed a single query per stock:
select * from stock_prices where stock_id = 1 order by created_at desc limit 1
Part 1: An inefficient query
Somewhat inefficient with 20+ queries, but it worked. The code (Laravel 6) was updated to use the correct relationships (stock hasMany stock_prices), which in turn generated a query like this:
select
*
from
`stock_prices`
where
`stock_prices`.`stock_id` in (1, 2, 3, 4, 5)
order by
`id` desc
While this saves on queries, it takes 1-2 seconds to run. Running explain shows it's still having to query 50k+ rows at any given time, even with the foreign key index. My next thought was that I'd add a limit to the query to only return the number of rows equal to the number of stocks I'm asking for. Query is now:
select
*
from
`stock_prices`
where
`stock_prices`.`stock_id` in (1, 2, 3, 4, 5)
order by
`id` desc
limit
5
Part 2: Query sometimes misses records
Performance is amazing - millisecond-level processing with this. However, it suffers from potentially not returning a price for one/ multiple of the stocks. Since the limit has been added, if any stock has more than one price (row) before the next stock, it will "consume" one of the row counts.
This is a very real scenario as some stocks pull data each minute, others every 15 minutes, etc. So there are cases where that above query, due to the limit will pull multiple rows for one stock and subsequently not return data for others:
| id | stock_id | value | change | created_at |
|------|----------|-------|--------|----------------|
| 5000 | 1 | 50 | 0.5 | 5/5/2020 17:00 |
| 5001 | 1 | 51 | 1 | 5/5/2020 17:01 |
| 6001 | 2 | 25 | 2.2 | 5/5/2020 17:00 |
| 6002 | 3 | 35 | 3.2 | 5/5/2020 17:00 |
| 6003 | 4 | 10 | 1.3 | 5/5/2020 17:00 |
In this scenario, you can see that stock_id of 1 has more frequent intervals of data, so when the query was ran, it returned two records for that ID, then continued down the list. After it hit 5 records, it stopped, meaning that stock id of 5 did not have any data returned, although it does exist. As you can imagine, that breaks things down the line in the app when no data was returned.
Part 3: Attempts to solve
The most obvious answer seems to be to add a GROUP BY stock_id as a way to require that I get the same number of results as I'm expected per stock. Unfortunately, this leads me back to Part 1, wherein that query, while it works, takes 1-2 seconds because it ends up having to traverse the same 50k+ rows as it did without the limit previously. This leaves me no better off.
The next thought was to arbitrarily make the LIMIT larger than it needs to be so it can capture all the rows. This is not a predictable solution since the query could be any combination of thousands of stocks that each have different intervals of data available. The most extreme example is stocks that pull daily versus each minute, which means one could have somewhere near 350+ rows before the second stock appears. Multiply that by the number of stocks in one query - say 50, and this still will require querying 15k+ plus rows. Feasible, but not ideal, and potentially not scalable.
Part 4: Suggestions?
Is it such a bad practice to have one API call initiate potentially 50+ DB queries just to get stock price data? Is there some thresehold of LIMIT I should use that minimizes the chances of failure enough to be comfortable? Are there other methods with SQL that would allow me to return the required rows without having to query a large chunk of tables?
Any help appreciated.
The fastest method is union all:
(select * from stock_prices where stock_id = 1 order by created_at desc limit 1)
union all
(select * from stock_prices where stock_id = 2 order by created_at desc limit 1)
union all
(select * from stock_prices where stock_id = 3 order by created_at desc limit 1)
union all
(select * from stock_prices where stock_id = 4 order by created_at desc limit 1)
union all
(select * from stock_prices where stock_id = 5 order by created_at desc limit 1)
This can use an index on stock_prices(stock_id, created_at [desc]). Unfortunately, when you use in, the index cannot be used as effectively.
Groupwise-max
SELECT b.*
FROM ( SELECT stock_id, MAX(created_at) AS created_at
FROM stock_proces
GROUP BY stock_id
) AS a
JOIN stock_prices AS b USING(stock_id, created_at)
Needed:
INDEX(stock_id, created_at)
If you can have two rows for the same stock in the same second, this will give 2 rows. See the link below for alternatives.
If that pair is unique, then make it the PRIMARY KEY and get rid of id; this will help performance, too.
More discussion: http://mysql.rjweb.org/doc.php/groupwise_max#using_an_uncorrelated_subquery

Combine results from 3 sql queries to calculate running stock

I am trying to calculate the stock by product a warehouse had over time. I have the information about today's stock, and also the amount of products sold and purchased by day. So, the calculation for yesterday values would be:
Yesterday_stock=Stock-yesterday_sold_quantity+yesterday_purchased_quantity. My problem is that i should save somewhere the amount of everyday's stock in order to calculate the stock of the previous day. I found that in order to do that i could use over sql clause with order by. But unfortunately, i have sql server 2008 and this is not a choice.
The tables are:
Prdamount which holds the current stock per product (StuPrdID ) and if it is blocked for some reason.
|-------------- |------------------|---------------
| StuPrdID | StuQAmount |prdBlockingReason
|---------------|------------------|-------------
| 12345| 16 |
|---------------|------------------|--------------
| 08889| 12 | expired
|---------------|------------------|------------
Table Moves which holds information about inserts and outputs of products. If MoveCase field has value equal 1 it is an output move, if it is a 2 it is a purchased quantity. Moves table dummy data:
|-------------- |--------------------- -|--------|-------
|MoveItemCode | MoveDate |MoveCase|MoveRealQty
|---------------|---------------------- |--------|-------
| 12345 |2018-06-24 00:00:00.000| 1 |14
|---------------|-----------------------|--------|--------
| 08889 |2018-06-24 00:00:00.000| 2 |578
|---------------|-----------------------|--------|--------
and table Product with information related with data:
|-------------- |------------------|
| PrdCode | PrdDespription |
|---------------|------------------|
| 12345| Orange juice|
|---------------|------------------|
| 08889| Chocolate|
|---------------|------------------|
I want an output like this:
|------------|--------------------- -|--------|--------------|------------
|Prdcode | PrdDescription |Stock |Stock 18/07/03|Stock 18/7/02
|------------|---------------------- |--------|--------------|------------
| 12345 |Orange Juice | 80 |50 34
|----------- |-----------------------|--------|--------------|------------
| 08889 |Chocolate | 45 |82 17
|------------|-----------------------|--------|--------------|-------------
this query gives me the running stock:
select
product.PrdCode,
product.PrdDescr,
SUM(StuQAmount) as Stock
from prdamount
left join product on (product.PrdID=prdamount.StuPrdID)
where prdamount.prdBlockingReason=' '
group by product.PrdCode,product.PrdDescr
order by product.PrdCode asc
This query gives me the quantity sold by product per day:
select
moves.MoveItemCode,
prd.PrdDescr,
moves.MoveDate,
SUM(MoveRealQty) as 'sold_quantity'
from moves
left join prd on (moves.MoveItemCode=product.PrdCode)
where (moves.MoveDate>'2018-06-01' and and moves.MoveCase=1)
group by moves.MoveItemCode,product.PrdDescr,moves.MoveDate
order by moves.MoveItemCode asc,moves.MoveDate asc
And this query gives me the quantity purchases by product per day:
select
moves.MoveItemCode,
prd.PrdDescr,
moves.MoveDate,
SUM(MoveRealQty) as 'Purchased_Quantity'
from Moves
left join product on (moves.MoveItemCode=product.PrdCode)
where (moves.MoveDate>'2018-06-01' and moves.MoveCase=2)
group by moves.MoveItemCode,product.PrdDescr,moves.MoveDate
order by moves.MoveItemCode asc,moves.MoveDate asc
I tried to combine these 3 queries into one using subqueries, but it didn't work. So how can i accomplish the result that i want? Sorry if the question is silly, i am a beginner in sql
try this,
select
product.PrdCode,
moves.MoveItemCode,
product.PrdDescr,
moves.MoveDate,
SUM( case when moves.MoveCase=1 then MoveRealQty else 0 end) as 'sold_quantity',
SUM( case when moves.MoveCase=2 then MoveRealQty else 0 end) as 'Purchased_Quantity',
(select SUM(StuQAmount) from prdamount where StuPrdID = product.PrdID and prdBlockingReason=' ')
from moves
left join product on (moves.MoveItemCode=product.PrdCode)
where (moves.MoveDate>'2018-06-01')
group by moves.MoveItemCode,product.PrdDescr,moves.MoveDate, product.PrdCode
order by moves.MoveItemCode asc,moves.MoveDate asc

Mysql Agregate function to select maximum and then select minimum price within that group

I am trying to get the maximum value out of a aggregate function, and then also get the min value out of a Price column which comes back in results.
id | discount | price
1 | 60 | 656
2 | 60 | 454
3 | 60 | 222
4 | 30 | 335
5 | 30 | 333
6 | 10 | 232
So in above table, I would like to separate Minimum Price vs Highest Discount.
This is the result I should be seeing:
id | discount | price
3 | 60 | 222
5 | 30 | 333
6 | 10 | 232
As you can see, its taken discount=60 group and separated the lowest price - 222, and the same for all other discount groups.
Could someone give me the SQL for this please, something like this -
SELECT MAX(discount) AS Maxdisc
, MIN(price) as MinPrice
,
FROM mytable
GROUP
BY discount
However, this doesnt separate the minimum price for each group. I think i need to join this table to itself to achieve that. Also, the table contains milions of rows, so the sql needs to be fast. One flat table.
This question is asked and answered with tedious regularity in SO. If only the algorithm was better at spotting duplicates. Anyway...
SELECT x.*
FROM my_table x
JOIN
( SELECT discount,MIN(price) min_price FROM my_table GROUP BY discount) y
ON y.discount = x.discount
AND y.min_price = x.price;
In your query, you cannot group by discount and then maximize the discount value.
This should get you the result you are looking for..
SELECT Max(ID) AS ID, discount, MIN(price) as MinPrice, FROM mytable GROUP BY discount
If you do not need the id, yo would do:
select discount, min(price) as minprice
from table t
group by discount;
If you want other columns in the row, you can either join back to the original table or use the substring_index()/group_concat() trick:
select substring_index(group_concat(id order by price), ',', 1) as id,
discount, min(price)
from table t
group by discount;
This will not always work because the intermediate result for group_concat() can overflow if there are too many matches within a column. This is controlled by a system parameter, which could be made bigger if necessary.

Custom select if sum>x

I have this table :
TICKETID | PRICE | NUMBER
So for each ticketid, the player can pay a price for each number on the ticketid.
So if the player wants to pay 1$, 3$ and 4$ for numbers 22,23 and 24 for ticketid 25, then the table will look like this :
TICKETID | PRICE | NUMBER
25 | 1 | 22
25 | 3 | 23
25 | 4 | 24
I want to select a random ticket that has TOTAL PRICE >50, to make it receive a prize.
I also want that the randomization to be fair, and that when doing this draw, each ticket would have only 1 apparition rate. If I don't use DISTINCT or GROUPBY, then a ticketid with 10 numbers will have more chances to get drawn than a ticket with 2 numbers.
I tried this but it's not working:
SELECT DISTINCT(ticketid),SUM(price) FROM table
WHERE SUM(price)>50 GROUP BY ticketid
I get the error message
invalid usage of GROUP BY function
Can anybody help?
What you want is the "HAVING" clause which is applied to any possible candidate records AFTER the group by aggregations have been processed. The Having is applied THEN and either included (or not) in the final result set.
SELECT
ticketid,
SUM(price) TotalPrice
FROM
table
group by
TicketID
HAVING
sum(price) > 50

Group-wise Maximum of a Certain Column

I've got the table:
SELECT * FROM shop;
+---------+--------+------
| article | dealer | price
+---------+--------+------
| 0001 | A | 3.45
| 0001 | B | 3.99
| 0002 | A | 10.99
| 0003 | B | 1.45
| 0003 | C | 1.69
| 0003 | D | 1.25
| 0004 | D | 19.95
+---------+--------+------
7 rows in set (0.20 sec)
And I want to get - for each article - the dealer or dealers with the most expensive price.
Could anyone tell me why this doesn’t work?
SELECT article, dealer, MAX(price) FROM shop GROUP BY(article);
For this query, I get the following result-set;
+---------+--------+------------+
| article | dealer | MAX(price) |
+---------+--------+------------+
| 0001 | A | 3.99 |
| 0002 | A | 10.99 |
| 0003 | B | 1.69 |
| 0004 | D | 19.95 |
+---------+--------+------------+
4 rows in set (0.03 sec)
Although the max prices are correct, I got the wrong dealers for some articles.
According to your question it seems that you have already read the article about group-wise maximum of a certain column, however you just don't understand why the method you mentioned does not work as you expect.
Let's imagine a query like this:
SELECT article, dealer, MAX(price), MIN(price)
FROM shop
GROUP BY article
What value of a dealer do you expect?
I think this answers your question.
Standard SQL would reject your query because you can not SELECT non-aggregate fields that are not part of the GROUP BY clause in an aggregate query.
You're using a MySQL extension of SQL described here:
MySQL extends the use of GROUP BY so that the select list can refer to
nonaggregated columns not named in the GROUP BY clause. This means
that the preceding query is legal in MySQL. You can use this feature
to get better performance by avoiding unnecessary column sorting and
grouping. However, this is useful primarily when all values in each
nonaggregated column not named in the GROUP BY are the same for each
group. The server is free to choose any value from each group, so
unless they are the same, the values chosen are indeterminate.
This does not work, because if you use group by, you can not use the individual fields of the original rows (except for the field you are grouping on). The correct way to do this, is to make an inner/nested query to select the dealer, suck as this (I haven't tested it, so it might be slightly off):
SELECT article, MAX(price) as maxPrice,
(SELECT dealer FROM shop AS s2 WHERE s2.article = s1.article AND s2.price = maxPrice) AS expensiveDealer
FROM shop AS s1 GROUP BY(article);
Here you go:
SELECT article, dealer, price
FROM (SELECT article, dealer, price
FROM shop
ORDER BY price DESC) AS h
GROUP BY article
This solution doesn't even require a MAX() function. :)
Note: This solution doesn't work with ONLY_FULL_GROUP_BY active and only works in MySQL. This solution is to a certain extent unsupported due to lack of documentation confirming this behavior. It works well for me and has always worked well for me however.
This method still works on the latest MySQL on sqlfiddle.
I just tumbled over this question and wonder why noone comes to idea to join the table with itself as described in certain tutorials (see links below).
So I'd suggest the following solution:
Select A.*
From shop As A
Left Join shop As B On A.article
= B.Article
AND A.price
< B.price
Where B.price Is Null;
The magic is obvious: join the table with itself and link any records in it to any other record having a higher price. From those, grab only those having NO linked record with a higher price (for these records are the ones with the highest price).
As far as I have experienced, this solution is even the best regarding its performance.
This part of the MySQL documentation and/or this very interesting article by Jan Kneschke might be helpful — enjoy!