mysql get row number of a specific result - mysql

My website allows users to record bids. Each bid is saved individually and associated to a user Id. A user can have many bids which are used to add up to one overall bid which is displayed upon the site.
What I am trying to do in sql is return the position a users overall bid is from a result set.
The sql I am using is below but problems arise when I use the group by command - the ordering seems to revert back to the default db order rather than by the sum of a users bid amounts:
SET #rowcount = 0;
SELECT rowCount, userId FROM (
SELECT userId, #rowcount := #rowcount + 1 as rowCount, sum(amount) as amount FROM bids group by userId order by amount desc
) t when product = xxxxx
appreciate if anyone knows if this is possible?

You need to move rowcount incrementation out of subquery. And put your WHERE condition inside, otherwise your subquery will sum bids on all products for a given user.
SET #rowcount = 0;
SELECT #rowCount:=#rowcount+1 as rowcount, userId, amount FROM
(
SELECT userId, sum(amount) as amount
FROM bids
WHERE product = xxxxx
GROUP BY userId
ORDER BY amount DESC
) t

Have you already tried?
SET #rowcount = 0;
SELECT rowCount, userId FROM (
SELECT userId, #rowcount := #rowcount + 1 as rowCount from
(select sum(amount) as amount, userId FROM bids group by userId) s
order by s.amount desc
) t where product = xxxxx

if I understand it correctly you could try something like this:
SELECT #rownum:=#rownum+1 ‘rank’, userId FROM (SELECT product, userId, sum(amount) AS amount FROM bids GROUP BY userId) t, (SELECT #rownum:=0) r WHERE t.product = xxxx ORDER BY t.amount DESC;

Related

How to display only the second purchase made per account

I have a transactions table which has shows various transactions made by several accounts. Some make only one, others more than that. At the moment the SQL I have prints out the first purchase of each account but i need it to print out the second made by each account
SELECT account_id
, purchase_date as second_purchase
, amount as second_purchase_amount
FROM Transactions t
WHERE purchase_date NOT IN (SELECT MIN(purchase_date)
FROM Transactions m
)
GROUP BY account_id
HAVING purchase_date = MIN(purchase_date);
What needs to change that the second purchase date and amount are chosen? I tried adding in a count for the account_id but it was giving me the wrong value.
You can use variables to assign row numbers and get the 2nd purchase.
SELECT account_id,purchase_Date,amount
FROM (
SELECT account_id
,purchase_date
,amount
--, #rn:=IF(account_id=#a_id and #pdate <> purchase_date,#rn+1,1) as rnum
,case when account_id=#a_id and #pdate <> purchase_date then #rn:=#rn+1
when account_id=#a_id and #pdate=purchase_date then #rn:=#rn
else #rn:=1 end as rnum
, #pdate:=purchase_date
, #a_id:=account_id
FROM Transactions t
CROSS JOIN (SELECT #rn:=0,#a_id:=-1,#pdate:='') r
ORDER BY account_id, purchase_date
) x
WHERE rnum=2
Explanation of how it works:
#rn:=0,#a_id:=-1,#pdate:='' - Declare 3 variables and initialize them, #rn for assigning the row numbers, #a_id to hold the account_id and #pdate to hold the purchase_date.
For the first row (ordered by account_id and purchase_date), account_id and #a_id, #pdate and purchase_date will be compared. As they wouldn't be equal, the when conditions fail and the else part would assign #rn=1. Also, the variable assignment happens after this. #aid and #pdate would be updated to current row's values. For the second row, if they are the same account and on a different date the first when condition will be executed and the #rn will be incremented by 1. If there are ties the second when condition would be executed and the #rn remains the same. You can run the inner query to check how the variables are assigned.
Number the rows and choose RowNumber = 2
select *
from (
select
#rn := case when #account_id = account_id then #rn + 1 else #rn := 1 end as RowNumber,
#account_id := account_id as account_id,
purchase_date
from
(select #rn := 1) x,
(select #acount_id :=account_id as account_id, purchase_date
from Transactions
order by account_id, purchase_date) y
) z
where RowNumber = 2;

Need to GROUP and COUNT records by a value, then qty > X gets DELETED to a certain qty

What I am trying to do is ORDER BY two fieds, usrid and ctoon_id, COUNT the number of ctoon_id per usrid, and then DELETE X records from a specified usrid wherever a ctoon_id count is greater than 50.
I'm sure there will be nested queries, but I don't have enough experience with SQL to make this work properly. Can someone please help?
To delete all records over a count of 50 wherever a ctoon_id count is greater than 50, could you try this query?
SELECT first, to confirm
First, run the SELECT, to make sure you're deleting the right records and that the syntax is correct on your database:
SELECT DISTINCT userid, ctoon_id
FROM (
SELECT
#row_number := CASE
WHEN #userid = usrid AND #ctoon_id = ctoon_id THEN #row_number + 1
ELSE 1
END AS rnum,
#userid:=usrid AS usrid,
#ctoon_id:=ctoon_id AS ctoon_id
FROM yourtable
ORDER BY usrid, ctoon_id
) sub
WHERE rnum > 50;
This query will:
Run the subquery to find the usrid and ctoon_id values and assign a rownumber to them
Select the DISTINCT values which have more than 50 records.
You can run the subquery separately first to see the impacted records.
DELETE records
To delete the records, wrap this SELECT in a DELETE query.
DELETE FROM yourtable
WHERE (usrid, ctoon_id) IN (
SELECT DISTINCT userid, ctoon_id
FROM (
SELECT
#row_number := CASE
WHEN #userid = usrid AND #ctoon_id = ctoon_id THEN #row_number + 1
ELSE 1
END AS rnum,
#userid:=usrid AS usrid,
#ctoon_id:=ctoon_id AS ctoon_id
FROM yourtable
ORDER BY usrid, ctoon_id
) sub
WHERE rnum > 50
);
Hope this is OK now.

How to get the maximum count, and ID from the table SQL

**castID**
nm0000116
nm0000116
nm0000116
nm0000116
nm0000116
nm0634240
nm0634240
nm0798899
This is my table (created as a view). Now I want to list the castID which has the most count (in this case which is nm0000116, and how many occurences/count it has in this table ( should be 5 times) and I'm not quite sure which query to use
try
Select CastId, count(*) countOfCastId
From table
Group By CastId
Having count(*)
= (Select Max(cnt)
From (Select count(*) cnt
From table
Group By CastId) z)
SELECT
MAX(E),
castId
FROM
(SELECT COUNT(castId) AS E,castId FROM [directors winning movies list] GROUP BY castId) AS t
You could just return the topmost count using LIMIT:
SELECT castID,
COUNT(*) AS Cnt
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
LIMIT 1
;
However, if there can be ties, the above query would return only one row. If you want all the "winners", you could take the count from the above query as a scalar result and compare it against all the counts to return only those that match:
SELECT castID,
COUNT(*) AS Cnt
FROM atable
GROUP BY castID
HAVING COUNT(*) = (
SELECT COUNT(*)
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
LIMIT 1
)
;
(Basically, same as Charles Bretana's approach, it just derives the top count differently.)
Alternatively, you could use a variable to rank all the counts and then return only those that have the ranking of 1:
SELECT castID,
Cnt
FROM (
SELECT castID,
COUNT(*) AS Cnt,
#r := IFNULL(#r, 0) + 1 AS r
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
) AS s
WHERE r = 1
;
Please note that with the above method the variable must either not exist or be pre-initialised with a 0 or NULL prior to running the query. To be on the safe side, you could initialise the variable directly in your query:
SELECT s.castID,
s.Cnt
FROM (SELECT #r := 0) AS x
CROSS JOIN
(
SELECT castID,
COUNT(*) AS Cnt,
#r := #r + 1 AS r
FROM atable
GROUP BY castID
ORDER BY Cnt DESC
) AS s
WHERE s.r = 1
;

mysql rank - row number where field is equal to X

This is my mysql DB:
id, auth, name, points
and what I want to get is to create a rank. So sort all the records by points, and get the number of the row where the auth field is equal to '1'
I were looking for this in stockoverflow archive, in google etc. However, I havn't find what I were looking for. I were trying to do it myself, but none of them didn't work for me.
Could anyone help me, please?
SELECT a.iterator, a.id, a.name, a.points
FROM (
SELECT #rank:=#rank+1 AS iterator, id, name, points, auth
FROM table, (SELECT #rank:=0) tmp
ORDER BY points DESC) a
WHERE a.auth = 1
This should give you the record with rank for the record with auth = 1:
SELECT * FROM
(
SELECT id, auth, name, points, #rownum := #rownum + 1 AS rank
FROM (
SELECT id, auth, name, points
FROM yourTable
ORDER BY points DESC
) a
JOIN (
SELECT #rownum := 0
) r
) b
WHERE b.auth = 1;
sqlfiddle demo

Complicated Query

I'm not sure if the following can be done using a mere select statement, but I have two tables (truncated with the data necessary to the problem).
Inventory Item
id int (PRIMARY)
quantity int
Stock - Contains changes in the stock of the inventory item (stock history)
id int (PRIMARY)
inventory_item_id int (FOREIGN KEY)
quantity int
created datetime
The quantity in stock is the change in stock, while the quantity in inventory item is the current quantity of that item
EVERYTHING IN THE running COLUMN WILL RETURN 0
SELECT
inventory_item.id,
(inventory_item.quantity - SUM(stock.quantity)) AS running
FROM
stock
JOIN
inventory_item ON stock.inventory_item_id = inventory_item.id
GROUP BY inventory_item.id
THE QUESTION
Now, what I would like to know is: Is it possible to select all of the dates in the stock table where the running quantity of the inventory_item ever becomes zero using a SELECT?
I know this can be done programmatically by simply selecting all of the stock data in one item, and subtracting the stock quantity individually from the current inventory item quantity, which will get the quantity before the change in stock happened. Can I do this with a SELECT?
(Updated) Assuming there will never be more than one record for a given combination of inventory_item_id and created, try:
SELECT i.id,
s.created,
i.quantity - COALESCE(SUM(s2.quantity),0) AS running
FROM inventory_item i
JOIN stock s ON s.inventory_item_id = i.id
LEFT JOIN stock s2 ON s2.inventory_item_id = i.id and s.created < s2.created
GROUP BY i.id, s.created
HAVING running=0
My take on it:
select
inventory_item_id `item`,
created `when`
from
(select
#total := CASE WHEN #curr <> inventory_item_id
THEN quantity
ELSE #total+quantity END as running_total,
inventory_item_id,
created,
#curr := inventory_item_id
from
(select #total := 0) a
(select #curr := -1) b
(select inventory_item_id, created, quantity from stock order by inventory_item_id, created asc) c
) running_total
where running_total.running_total = 0;
This one has the relative advantage of having to give only one pass to the stock table. Depending on the size and the indexes on it that may or may not be a good thing.
The most logical way to do this is with a cumulative sum. But, MySQL doesn't support that.
The clearest approach, in my opinion, is to use a correlated subquery to get the running quantity. Then it is a simple matter of a where clause to select where it is 0:
select i.*
from (select i.*,
(select SUM(i2.inventory)
from inventory i2
where i2.inventory_item_id = i.inventory_item_id and
i2.created <= i.created
) as RunningQuantity
from inventory i
) i
where RunningQuantity = 0;
I had a response similar based on a running total to be flagged found here...
You can do with MySQL #variables, but the data needs to be pre-queried and ordered by the data of activity... then set a flag on each row that causes the negative and keep only those. Something like
select
PreQuery.*
from
( select
s.id,
s.created,
#runBal := if( s.id = #lastID, #runBal - quantity, #i.quantity ) as CurBal,
#lastID := s.id as IDToCompareNextEntry
from
stock s
join inventory_item ii
on s.inventory_item_id = ii.id,
(select #lastID := -1,
#runBal := 0 ) sqlvars
order by
s.id,
s.created DESC ) PreQuery
where
PreQuery.CurBal < 0
This way, for each inventory item, it works backwards by created date (order by the created descending per ID). So, when the inventory ID changes, look to the inventory table "Quantity" field to START the tally of used stock down. If same ID as the last record processed, just use the running balance and subtract out the quantity of that stock entry.
I believe this is a simple approach to this.
SELECT inventory_item.id, stock.created
FROM inventory_item
JOIN stock ON stock.inventory_item_id = inventory_item.id
WHERE (SELECT SUM(quantity) FROM stock WHERE created <= stock.created) = 0