Script outputs random value (winning ticket) from 0 to 5000 (in this case). Lets take 3001. I need a query to take a row where id = 4, because it has the winning ticket. How can I do it?
More info:
id 1 has tickets from 0 to 1000
id 2 has tickets from 1000 to 2000
id 3 has tickets from 2000 to 3000
id 4 has tickets from 3000 to 3500 <----- The winning ticket
id 5 has tickets from 3500 to 5000
Mysql table:
+-----------------------+
| id | Userid | Ticket |
+-----------------------+
| 1 | 1234567 | 1000 | // 0 to 1000
| 2 | 1234567 | 1000 | // 1000 to 2000
| 3 | 1234567 | 1000 | // 2000 to 3000
| 4 | 9876543 | 500 | // 3000 to 3500
| 5 | 1234567 | 1500 | // 3500 to 5000
+-----------------------+
Does the following query work for you :
set #winningticket=3001;
set #cstart=0;
SELECT id, Userid, (lastticket-tickets+1) firstticket, lastticket
FROM
(
SELECT id, Userid, Ticket as tickets, (#cstart := #cstart+Ticket) lastticket
FROM mytable
order by id) a
having #winningticket between firstticket and lastticket;
I have made the assumption that id=1 has tickets from 1-1000, id=2 has tickets from 1001 to 2000 - rather than the values you give - otherwise ticket 1000 is owned by id=1 AND id=2, 2000 by id=2 AND id=3 etc - if that is incorrect remove the +1 from the calculation for firstticket & the values will match yours.
The inner subquery creates a cumulative total for the final ticket number owned by that id using a user variable.
The outer query calculates the start ticket number - note this cannot be reliably done in the inner query as the first & last ticket numbers both rely on the same user variable & order of evaluation of columns is not guaranteed & use of the same user variable in the select list is advised against if it is modified in any way.
The outer query uses a having clause to select the winning ticket between first & last ticket number.
Related
I have a table of owners
id | owner
--------
1 | Jack
2 | Lee
3 | Daniel
and a table of their transactions
id | owner_id | change
----------------
1 | 1 | 500
2 | 2 | 300
3 | 1 | -100
4 | 2 | 100
5 | 2 | -300
and I'm trying to get the balance of Jack's account. So for example here I would return
500
400
as Jack will first have 500 as his balance and after the change he will have 400.
What I currently have is
SELECT O.id, change FROM Owners O, Transactions WHERE O.id = 1 & Transactions.owner_id = 1;
but I can only get the rows of Jack's change. What can I do to get the balance for each row?
If you are using MySQL 8, you can do something like this:
SELECT SUM(change_v) OVER(ORDER BY id) AS balance
FROM transactions
WHERE owner_id = 1
An alternative could be to use a variable, like this:
SET #balance := 0;
SELECT (#balance := #balance + change_v) AS balance
FROM transactions
WHERE owner_id = 1
ORDER BY id
Keep in mind that change is a reserved word in MySQL, that's why I simply used the name change_v. There are ways you can use reserved words for column names, but I wouldn't advise you to do so.
I have two tables in my db: accounts and transactions. They look more or less like this, with relevant columns and example entries:
accounts
id | uid |
------------------
1 | "abcde" |
2 | "qwert" |
transactions
id | account_id | amount | balance |
------------------------------------
1 | 1 | 100 | 100 |
2 | 1 | 200 | 300 |
3 | 2 | 500 | 500 |
4 | 2 | 300 | 800 |
So basically Account has many Transactions, and the transaction tells what the amount was + it shows the balance after adding that transaction to the previous balance. So last transaction tells us what the current balance on the account is.
Now I would like to fetch an account, having only its uid, but with a twist - I'd like to also fetch the balance, which would be the balance column of the last transaction (based on id) that belongs to that account. Ideally this will give me an object of the Account model, with the additional balance param accessible using object[:balance].
I know that this would require a left outer join and a select that adds the balance param to the object, something like select('accounts.*', 'transactions.balance as balance') - plus picking up the last one, so ordering by id and limiting to 1 - but I have no idea how to construct this query (using activerecord methods or pure SQL), considering the fact that I only have access to the uid of the account, not its id which is referenced as the account_id.
You can try something along these lines:
WITH ranked AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY account_id ORDER BY id DESC) AS tr
FROM transactions AS t inner join accounts a on a.id = t.id
where a.uid = 'qwert'
)
SELECT * FROM ranked WHERE tr = 1;
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
I have a table called updates which has the distance of a vehicle at the captured_at date. Using MySQL, How can I get the SUM of differences between the first captured update and the latest captured update per vehicle.
updates table:
id | vehicle_id | distance | captured_at
1 | 1 | 100 | 2018-02-10
2 | 1 | 50 | 2018-02-05
3 | 1 | 75 | 2018-02-07
4 | 2 | 200 | 2018-02-07
5 | 2 | 300 | 2018-02-09
The result I'm expecting is:
(100-50) + (300-200) = 150
One thing to keep in mind is that a bigger ID does not necessarily mean that it's the latest update as you can see in the example above.
(Comment: naming your tables with reserved words is a bad idea)
Getting the smallest and largest values is trivial:
SELECT vehicle_id, MAX(distance) - MIN(distance)
FROM `updates`
GROUP BY vehicle_id;
Adding these values is trivial when you know that a SELECT query can be used n place of a table - but you also need to create aliases for the aggregated attributes:
SELECT SUM(diff)
FROM (
SELECT vehicle_id, MAX(distance) - MIN(distance) AS diff
FROM `updates`
GROUP BY vehicle_id
) AS src
I have two tables (Invoices and taxes) in mysql:
Invoices:
- id
- account_id
- issued_at
- total
- gross_amount
- country
Taxes:
- id
- invoice_id
- tax_name
- tax_rate
- taxable_amount
- tax_amount
I'm trying to retrive a report like this
rep_month | country | total_amount | tax_name | tax_rate(%) | taxable_amount | tax_amount
--------------------------------------------------------------------------------------
2017-01-01 | ES | 1000 | TAX1 | 21 | 700 | 147
2017-01-01 | ES | 1000 | TAX2 | -15 | 700 | 105
2016-12-01 | FR | 100 | TAX4 | 20 | 30 | 6
2016-12-01 | FR | 100 | B2B | 0 | 70 | 0
2017-01-01 | GB | 2500 | TAX3 | 20 | 1000 | 200
The idea behind this is that an invoice has a has_many relation with taxes. So an invoice can have or not taxes. The report should show the total amount collected (total_amount) for a given country (regardess if it includes taxes)
and indicate which part of that total amount is taxable (taxable_amount) for an specific tax.
My current approach is this one:
SELECT
DATE_FORMAT(invoices.issued_at, '%Y-%m-01') AS rep_month,
invoices.country AS country
( SELECT sum(docs.gross_amount)
FROM invoices AS docs
WHERE docs.country = invoices.country
AND DATE_FORMAT(docs.issue_date, '%Y-%m-01') = rep_month
) AS total_amount,
taxes.tax_name AS tax_name,
taxes.tax_rate AS tax_rate,
SUM(taxes.taxable_amount) AS taxable_amount,
SUM(taxes.tax_amount) AS tax_amount
FROM invoices
JOIN taxes ON invoices.id = taxes.document_id
AND documents.issue_date BETWEEN '2016-01-01' AND '2017-12-31'
GROUP BY account_id, rep_month, country, tax_name, tax_rate
ORDER BY country desc
Well, this works but for a real dataset (thousands of records) it's really slow as the select subquery for retrieving the total_amount is being run for each row of the report.
I cannot make a LEFT JOIN taxes with a direct SUM(gross_amount) as the GROUP BY groups by tax name and rate and I need to show the total collected per country regardless if the amount was taxed or not. Is there a faster alternative to this?
I do not know the exact use case of using this query but the issue is the way with which you're trying to structure the DB, you're trying to get the entire data in one go.
Ideally, you should run the query you have and store in a different table (summary table) and then query directly from the summary table whenever you want. And if you have a new entry in the Invoices table then you can use the query to run either on every entry or periodically update the summary table via a cronjob.