MySQL query to get first 10 in total `qty` - mysql

So, my table is :
*uid
code
qty
1
CODE2200
5
2
CODE2205
6
3
CODE0002
2
...
...
...
I want to fetch only first maximum of 10 from qty.
The result i want is :
*uid
code
qty
1
CODE2200
5
2
CODE2205
6
What i know is, limit is for counting the first 'n' row, so it cant be used here.
I'm trying to use WHERE SUM(qty) <= 10 also not work.

You can use window function :
select uid, code, qty
from (select t.*, sum(qty) over(order by uid) as qtysum
from t
) t
where qtysum <= 10;

You can use a cumulative sum. To get the first row that exceeds 10:
select t.*
from (select t.*,
sum(qty) over (order by uid) as running_qty
from t
) t
where running_qty - qty < 10;
If you wanted the first row that hits the threshold of 10:
select t.*
from (select t.*,
sum(qty) over (order by uid) as running_qty
from t
) t
where running_qty - qty < 10 and
qty >= 10;

Related

get the most common value for each column

I'm attempting to create an SQL query that retrieves the total_cost for every row in a table. Alongside that, I also need to collect the most dominant value for both columnA and columnB, with their respective values.
For example, with the following table contents:
cost
columnA
columnB
target
250
Foo
Bar
XYZ
200
Foo
Bar
XYZ
150
Bar
Bar
ABC
250
Foo
Bar
ABC
The result would need to be:
total_cost
columnA_dominant
columnB_dominant
columnA_value
columnB_value
850
Foo
Bar
250
400
Now I can get as far as calculating the total cost - that's no issue. I can also get the most dominant value for columnA using this answer. But after this, I'm not sure how to also get the dominant value for columnB and the values too.
This is my current SQL:
SELECT
SUM(`cost`) AS `total_cost`,
COUNT(`columnA`) AS `columnA_dominant`
FROM `table`
GROUP BY `columnA_dominant`
ORDER BY `columnA_dominant` DESC
WHERE `target` = "ABC"
UPDATE: Thanks to #Barmar for the idea of using a subquery, I managed to get the dominant values for columnA and columnB:
SELECT
-- Retrieve total cost.
SUM(`cost`) AS `total_cost`,
-- Get dominant values.
(
SELECT `columnA`
FROM `table`
GROUP BY `columnA`
ORDER BY COUNT(*) DESC
LIMIT 1
) AS `columnA_dominant`,
(
SELECT `columnB`
FROM `table`
GROUP BY `columnB`
ORDER BY COUNT(*) DESC
LIMIT 1
) AS `columnB_dominant`
FROM `table`
WHERE `target` = "XYZ"
However, I'm still having issues figuring out how to calculate the respective values.
You might get close, if we want to get percentage values we can try to add COUNT(*) at subquery to get max count by columnA and columnB then do division by total count
SELECT
SUM(cost),
(
SELECT tt.columnA
FROM T tt
GROUP BY tt.columnA
ORDER BY COUNT(*) DESC
LIMIT 1
) AS columnA_dominant,
(
SELECT tt.columnB
FROM T tt
GROUP BY tt.columnB
ORDER BY COUNT(*) DESC
LIMIT 1
) AS columnB_dominant,
(
SELECT COUNT(*)
FROM T tt
GROUP BY tt.columnA
ORDER BY COUNT(*) DESC
LIMIT 1
) / COUNT(*) AS columnA_percentage,
(
SELECT COUNT(*)
FROM T tt
GROUP BY tt.columnB
ORDER BY COUNT(*) DESC
LIMIT 1
) / COUNT(*) AS columnB_percentage
FROM T t1
If your MySQL version supports the window function, there is another way which reduce table scan might get better performance than a correlated subquery
SELECT SUM(cost) OVER(),
FIRST_VALUE(columnA) OVER (ORDER BY counter1 DESC) columnA_dominant,
FIRST_VALUE(columnB) OVER (ORDER BY counter2 DESC) columnB_dominant,
FIRST_VALUE(counter1) OVER (ORDER BY counter1 DESC) / COUNT(*) OVER() columnA_percentage,
FIRST_VALUE(counter2) OVER (ORDER BY counter2 DESC) / COUNT(*) OVER() columnB_percentage
FROM (
SELECT *,
COUNT(*) OVER (PARTITION BY columnA) counter1,
COUNT(*) OVER (PARTITION BY columnB) counter2
FROM T
) t1
LIMIT 1
sqlfiddle
try this query
select sum(cost) as total_cost,p.columnA,q.columnB,p.columnA_percentage,q.columnB_percentage
from get_common,(
select top 1 columnA,columnA_percentage
from(
select columnA,count(columnA) as count_columnA,cast(count(columnA) as float)/(select count(columnA) from get_common) as columnA_percentage
from get_common
group by columnA)s
order by count_columnA desc
)p,
(select top 1 columnB,columnB_percentage
from (
select columnB,count(columnB) as count_columnB, cast(count(columnB) as float)/(select count(columnB) from get_common) as columnB_percentage
from get_common
group by columnB) t
order by count_columnB desc)q
group by p.columnA,q.columnB,p.columnA_percentage,q.columnB_percentage
so if you want to get the percent and dominant value you must make their own query like this
select top 1 columnA,columnA_percentage
from(
select columnA,count(columnA) as count_columnA,cast(count(columnA) as float)/(select count(columnA) from get_common) as columnA_percentage
from get_common
group by columnA)s
order by count_columnA desc
then you can join with the sum query to get all value you want
hope this can help you

How to return rows that sum(field) <= value

The concept is to find the rows in which sum(fCurrAmt) may higher than the entered amount but should not lower than entered amount. I dont know how to explain indetail this creteria.
Lets say I have a table demo
Scenario : 1
id fCurrAmt price
------------------
1 1 10
2 1 20
3 2 25
4 3 30
If the entered amount is 3, I need to return first 3 rows
id fCurrAmt price
------------------
1 1 10
2 1 20
3 2 25
In the above scenario, sum(fCurrAmt) is 4 which is higher than entered amount.
Scenario : 2
id fCurrAmt price
------------------
1 1 10
2 1 20
If the entered amount is 3, I need to return there is no records.
In the above scenario, sum(fCurrAmt) is 2 which is lower than entered amount.
I have tried with below code in scenario 1
SELECT a.id,a.price,a.total,a.fCurrAmt from (
select b.id,b.price,b.fCurrAmt,(
select sum(fCurrAmt) from demo c where c.id <= b.id order by c.id
) as total from demo b
) a where a.total <= 3
It returns first 2 records only
Try This You need to use subquery and min with group by. Using subquery we can return the minimum id where the sum is satisfied with given numbers and then join the id to retrieve full rows upto the id
SELECT *
FROM test t
INNER JOIN(
SELECT MIN(id) valId
FROM (
SELECT t.id,
(SELECT SUM(t1.fCurrAmt)
FROM test t1
WHERE t1.id <= t.id) AS Rowsum
FROM test t) t2
WHERE Rowsum >= 3) t1 ON t1.valId >= t.id;
SQL Fiddle http://www.sqlfiddle.com/#!9/a1d07/13
Try this
DECLARE #sumOfFCurrAmt int
DECLARE #sumOfEnteredAmt int
set #sumOfFCurrAmt=(select Sum(fCurrAmt) from demoB)
set #sumOfEnteredAmt=(select sum(fCurrAmt) from demoC)
IF(#sumOfFCurrAmt>#sumOfEnteredAmt)
BEGIN
SELECT top(#sumOfEnteredAmt)* FROM demoB
END
A slightly lengthy way but it works.
First, I would store the sum of fCurrAmt up to the number entered in a temporary table. Hence, the first three statements are DROP, CREATE and INSERT. I would then take that value to check if the sum of those rows until the number entered are greater or lesser, if it is greater, then return all the rows until the threshold else return nothing. Here, sof12 is the same table as scenario 1 and sof14 is the same table as yours in scenario 2.
SCENARIO 1:
DROP TABLE IF EXISTS `tempsum`;
CREATE TABLE tempsum (`sum` integer(13));
INSERT INTO tempsum (SELECT SUM(fCurrAmt) FROM
(SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof12
WHERE #total <= 3) as new2);
SELECT id, fCurrAmt, price FROM (
SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof12
WHERE #total <= 3) As new3 HAVING (SELECT SUM(SUM) FROM tempsum) >= 3;
Output of this case:
id fCurrAmt price
1 1 10
2 1 20
3 2 25
SCENARIO 2:
DROP TABLE IF EXISTS `tempsum`;
CREATE TABLE tempsum (`sum` integer(13));
INSERT INTO tempsum (SELECT SUM(fCurrAmt) FROM
(SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof14
WHERE #total <= 3) as new2);
SELECT id, fCurrAmt, price FROM (
SELECT NULL AS id, NULL AS fCurrAmt, NULL AS price, NULL AS total
FROM dual
WHERE (#total := 0)
UNION
SELECT id, fCurrAmt, price, #total := #total + fCurrAmt AS total
FROM sof14
WHERE #total <= 3) As new3 HAVING (SELECT SUM(SUM) FROM tempsum) >= 3;
Output of this case: No records returned.

How to select last and last but one records

I have a table with 3 columns id, type, value like in image below.
What I'm trying to do is to make a query to get the data in this format:
type previous current
month-1 666 999
month-2 200 15
month-3 0 12
I made this query but it gets just the last value
select *
from statistics
where id in (select max(id) from statistics group by type)
order
by type
EDIT: Live example http://sqlfiddle.com/#!9/af81da/1
Thanks!
I would write this as:
select s.*,
(select s2.value
from statistics s2
where s2.type = s.type
order by id desc
limit 1, 1
) value_prev
from statistics s
where id in (select max(id) from statistics s group by type) order by type;
This should be relatively efficient with an index on statistics(type, id).
select
type,
ifnull(max(case when seq = 2 then value end),0 ) previous,
max( case when seq = 1 then value end ) current
from
(
select *, (select count(*)
from statistics s
where s.type = statistics.type
and s.id >= statistics.id) seq
from statistics ) t
where seq <= 2
group by type

Aggregating query on Table?

I am stuck in one point i have to pick the data from the customer table which has values of customer_id and amount_paid . I want to show a result in the form that first 3 values of the user should be visible in a column name group as a text Group1 and 4 to 10 values of that user to get text Group2 and rest Group3 .
Can you please tell me how to group the values for every customer ?
Thanks
I want to show a result in the form that first 3 values of the user should be visible in a column name group as a text Group1 and 4 to 10 values of that user to get text Group2 and rest Group3
Below is for BigQuery Standard SQL
#standardSQL
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
-- ORDER BY user_id, grp
You can test / play with below dummy generated data
#standardSQL
WITH users AS (
SELECT user_id FROM UNNEST(GENERATE_ARRAY(1,5)) user_id
),
amounts AS (
SELECT ROUND(50 * RAND()) amount_paid FROM UNNEST(GENERATE_ARRAY(1,50)) amount_paid
),
customer AS (
SELECT user_id, ROUND(amount_paid * RAND()) amount_paid
FROM users
CROSS JOIN amounts
)
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
ORDER BY user_id, grp
The output will look like below
user_id grp amount_paid
1 1 147.0
1 2 323.0
1 3 879.0
2 1 147.0
2 2 323.0
2 3 879.0
. . .
so you still need calculate share which (from your question and hopefully) is not a problem for you
added share calculation
#standardSQL
WITH grps AS (
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
)
SELECT * ,
ROUND(amount_paid / SUM(amount_paid) OVER(PARTITION BY user_id), 3) share
FROM grps
-- ORDER BY user_id, grp

MS Access max date and recent date and their respective qty

Looking for MaxDate, its most recent date and the interval between. Of the MaxDate and most recent, I also need the quantities for each so I can also find the interval
Table "tblITEM_InventoryCount" structure is as follows:
Item_No Count Date Qty
001 08/29/2015 12
001 08/15/2015 17
001 07/15/2015 19
Item No 001
Max(CountDate) 08/29/2015
PriorCountDate 08/15/2015
Interval Days (MaxDate-RecentDate) 14
MaxDate Quantity 12
PriorCountDate Quantity 17
Interval Qty (17-12) 5
Currently using a query to find last two count dates for each ITEM_NO
SELECT tblITEM_InventoryCount.ITEM_NO, tblITEM_InventoryCount.Quantity, tblITEM_InventoryCount.CountDate
FROM tblITEM_InventoryCount
WHERE (((tblITEM_InventoryCount.CountDate)>=NthInGroup([tblITEM_InventoryCount].[ITEM_NO],2)))
ORDER BY tblITEM_InventoryCount.ITEM_NO, tblITEM_InventoryCount.CountDate DESC;
Then I am using a second query to calculate my data:
SELECT qryLAST2_InventoryCount_TRANSACTIONS.ITEM_NO, qryLAST2_InventoryCount_TRANSACTIONS.CountDate, (SELECT MAX([CountDate]) FROM [qryLAST2_InventoryCount_TRANSACTIONS] AS [Old Orders] WHERE [Old Orders].[CountDate] < [qryLAST2_InventoryCount_TRANSACTIONS].[CountDate] AND [Old Orders].[ITEM_NO] = [qryLAST2_InventoryCount_TRANSACTIONS].[ITEM_NO]) AS PriorCountDate, [CountDate]-[PriorCountDate] AS DaysInterval, qryLAST2_InventoryCount_TRANSACTIONS.Quantity, (SELECT Last([Quantity]) FROM [qryLAST2_InventoryCount_TRANSACTIONS] AS [OldCount] WHERE [OldCount].[Quantity] < [qryLAST2_InventoryCount_TRANSACTIONS].[Quantity] AND [OldCount].[ITEM_NO] = [qryLAST2_InventoryCount_TRANSACTIONS].[ITEM_NO]) AS PriorQuantity, [Quantity]-[PriorQuantity] AS QuantityInterval, [QuantityInterval]*30/[DaysInterval] AS [Usage]
FROM qryLAST2_InventoryCount_TRANSACTIONS
GROUP BY qryLAST2_InventoryCount_TRANSACTIONS.ITEM_NO, qryLAST2_InventoryCount_TRANSACTIONS.CountDate, qryLAST2_InventoryCount_TRANSACTIONS.Quantity
ORDER BY qryLAST2_InventoryCount_TRANSACTIONS.ITEM_NO, qryLAST2_InventoryCount_TRANSACTIONS.CountDate DESC;
I am not getting the results I need. The query returns two record lines for each item along with their max or last countdate, the previous countdate, intervaldays, qty, last qty, and interval.
I need max or last countdate and its qty count and prior countdate and its qty count.
Any help would be greatly appreciated.
Try this :
select
tab.item_no,
tab.Max_Date,
tab.PriorCountDate,
DateDiff ("d", tab.Max_Date, tab.PriorCountDate) as IntervalDays,
tab.MaxDateQty,
tab.PriorCountDateQty,
( tab.MaxDateQty-tab.PriorCountDateQty) as IntervalQty,
from
( select
temp1.item_no as item_no,
temp1.m as Max_Date,
(select MAX(count_date) from tblITEM_InventoryCount where count_date <> temp1.m ) as PriorCountDate,
temp1.q as MaxDateQty,
(select MAX(Qty) from tblITEM_InventoryCount where Qty <> temp1.q) as PriorCountDateQty,
from
( select item_no as i, Qty as q, MAX(count_date) as m
group by item_no, Qty ) as temp1
inner join
tblITEM_InventoryCount as temp2
on
temp1.item_no = temp2.item_no
)
as tab ;