Calculating running balance with limit - php

I am working on a small accounting app. The following function is used to get transaction of a supplier. I want get the balance after each transaction in this loop. Can some one provide me a best method.
public function transaction_by_id_paginated($supplier_id,$start_from,$limit) {
$sql="
SELECT *
FROM transactions
WHERE tr_sup_id = $supplier_id
ORDER
BY tr_date DESC
LIMIT $start_from, $limit
";
$suppliers = DB::fetch($sql);
return $suppliers;
}
With this function i want get a calculated balance value. I am not storing balance in DB.
Fields in transactions tables are
tr_id (Primary) int(11)
tr_sup_id int(11)
bill_id int(11)
tr_date date
tr_type varchar(250)
tr_bill int(250)
tr_payment int(250)
tr_payment_note varchar(250)
i want balance which is equal to balance = balance + tr_payment - tr_bill

Consider the following:
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT i, #j:=#j+i FROM ints, (SELECT #j:=0) vars ORDER BY i;
+---+----------+
| i | #j:=#j+i |
+---+----------+
| 0 | 0 |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
+---+----------+
10 rows in set (0.00 sec)
SELECT i, #j:=#j+i FROM ints, (SELECT #j:=0) vars ORDER BY i LIMIT 3,6;
+---+----------+
| i | #j:=#j+i |
+---+----------+
| 3 | 3 |
| 4 | 7 |
| 5 | 12 |
| 6 | 18 |
| 7 | 25 |
| 8 | 33 |
+---+----------+
6 rows in set (0.00 sec)

Related

MySQL - Select next row if previous row field was 1, select first row if next row does not exist?

I have a table as below:
CREATE TABLE IF NOT EXISTS `room_players`
(`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY
,`player_id` int(11) NOT NULL
,`room_id` tinyint(1) NOT NULL
,`dealer` tinyint(1) NOT NULL
);
INSERT INTO room_players (`player_id`, `room_id`, `dealer`) VALUES
(1, 1, '0'),
(2, 1, '0'),
(3, 1, '0'),
(4, 1, '1'),
(5, 1, '0');
I need to get row after WHERE dealer='1' AND room_id='1'; in this case, it should return player_id = 5.
Now, when dealer = 1 for player_id = 5, then it should select the first player_id of the current room_id.
I tried:
SELECT *
FROM room_players
WHERE room_id='1'
AND id IN ( SELECT ID+1
FROM room_players
WHERE room_id='1'
AND dealer != 1
)
Selecting the next row works with this query but, when we got to the last player in line, it will return nothing.
I could run 2 different queries to get it work, but I would like to use an eloquent single query.
Using window functions we can achieve this in 1 query pretty easily:
first_value() asending id
first_value() descending id
lead()
case expression to figure out when to display next dealer value.
Just note: elegance of 1 query may make maintenance more complex.
it's not always better to build 1 sometimes two IS simplier.
Demo
you can adjust dealer to wherever you want and it will return the next dealer player number on the dealer number line. you didn't define expected results so I am unsure what you result is to look like.
SELECT *
, case WHEN dealer=1 and player_id = first_value(player_id) over (partition by room_id order by id desc)
THEN first_value(player_id) over (partition by room_id order by id)
WHEN dealer=1
THEN lead(player_id) over (partition by room_id order by id) end as NextDealer
From room_players
Giving us:
+----+-----------+---------+--------+------------+
| id | player_id | room_id | dealer | NextDealer |
+----+-----------+---------+--------+------------+
| 1 | 1 | 1 | 0 | |
| 2 | 2 | 1 | 1 | 3 |
| 3 | 3 | 1 | 0 | |
| 4 | 4 | 1 | 0 | |
| 5 | 5 | 1 | 0 | |
+----+-----------+---------+--------+------------+
or
+----+-----------+---------+--------+------------+
| id | player_id | room_id | dealer | NextDealer |
+----+-----------+---------+--------+------------+
| 1 | 1 | 1 | 0 | |
| 2 | 2 | 1 | 0 | |
| 3 | 3 | 1 | 0 | |
| 4 | 4 | 1 | 0 | |
| 5 | 5 | 1 | 1 | 1 |
+----+-----------+---------+--------+------------+
depending on who is dealer..
DB Fiddle expanded to show what's happening with case expression & show how to get dealer/room. Yes I had to use a CTE because the window functions can't have the data limited; it first must have the data materialized. But, if you create the CTE as a view and you're simply passing in the room to get the next dealer this works fine imo.
We can use:
WITH CTE AS (
SELECT *,
case WHEN dealer=1 and player_id = first_value(player_id) over (partition by room_id order by id desc)
THEN first_value(player_id) over (partition by room_id order by id)
WHEN dealer=1
THEN lead(player_id) over (partition by room_id order by id) end NextDealer
From room_players)
SELECT room_id, NextDealer FROM CTE WHERE NextDealer is not null
to get data by room and next dealer giving us: (I added some additional test data to make sure certain edge cases were working as expected)
+---------+------------+
| room_id | NextDealer |
+---------+------------+
| 1 | 7 |
| 2 | 99 |
+---------+------------+
With Date of:
+----+-----------+---------+--------+
| id | player_id | room_id | dealer |
+----+-----------+---------+--------+
| 1 | 1 | 1 | 0 |
| 2 | 2 | 1 | 0 |
| 3 | 3 | 1 | 0 |
| 4 | 4 | 1 | 1 |
| 5 | 7 | 1 | 0 |
| 6 | 99 | 2 | 0 |
| 7 | 14 | 2 | 0 |
| 8 | 22 | 2 | 0 |
| 9 | 77 | 2 | 1 |
| 10 | 15 | 2 | 0 |
+----+-----------+---------+--------+
I get:
+----+-----------+---------+--------+----+----+----+------------+
| id | player_id | room_id | dealer | a | b | c | NextDealer |
+----+-----------+---------+--------+----+----+----+------------+
| 1 | 1 | 1 | 0 | 7 | 1 | 2 | |
| 2 | 2 | 1 | 0 | 7 | 1 | 3 | |
| 3 | 3 | 1 | 0 | 7 | 1 | 4 | |
| 4 | 4 | 1 | 1 | 7 | 1 | 7 | 7 |
| 5 | 7 | 1 | 0 | 7 | 1 | | |
| 6 | 99 | 2 | 0 | 15 | 99 | 14 | |
| 7 | 14 | 2 | 0 | 15 | 99 | 22 | |
| 8 | 22 | 2 | 0 | 15 | 99 | 77 | |
| 9 | 77 | 2 | 1 | 15 | 99 | 15 | 15 |
| 10 | 15 | 2 | 0 | 15 | 99 | | |
+----+-----------+---------+--------+----+----+----+------------+
I went with slightly different approach, i got to solve it with one simple query, but did some coding with PHP also. Its not the eloquant i wanted, but it works for me.
function dealer($room_id) {
global $con;
$array = array();
$i = 0;
$cd = null;
$query = mysqli_query($con, "SELECT * FROM room_players WHERE room_id='".$room_id."'");
while($ft = mysqli_fetch_array($query)) {
$array[] = array("user_id" => $ft['user_id']);
if($ft['dealer'] == 1) {
$cd = $i;
}
$i++;
}
if(!is_null($cd)) {
if($array[$cd+1]) {
$next = $array[$cd+1];
}else{
$next = $array[0];
}
}else{
$next = $array[array_rand($array, 1)];
}
return $next['user_id'];
}
echo "Next dealer is: ". dealer(1);

generate sequential reference numbers

I am generating sequential reference numbers like complaint/1, complaint/2 when an insertion is made. I get what the previously generated value is (complaint/2) and then increment.
But when two users submit at the same time, I sometimes get same reference nos for both complaints.
How do I prevent this?
SELECT RIGHT(Date_format(from_financial_year_,'%Y'),2),
RIGHT(Date_format(to_financial_year_,'%Y'),2)
INTO from_financial_year_,
to_financial_year_;SELECT rec.receipt_ref_no
INTO last_receipt_ref_num_
FROM svk_apt_receipts rec
WHERE Replace(Substring_index(rec.receipt_ref_no, '/', 2),'REC/','') = Concat(from_financial_year_,to_financial_year_)
AND rec.customer_id = customer_id_
AND rec.association_id = association_id_
ORDER BY rec.receipt_id DESC limit 1;IF(last_receipt_ref_num_ IS NULL) then
SELECT 1
INTO max_ref_id_;
else
SELECT (replace(last_receipt_ref_num_, concat('REC/',from_financial_year_,to_financial_year_,'/'),'')+1)
INTO max_ref_id_;ENDIF;SELECT Concat('REC/',from_financial_year_,to_financial_year_,'/',max_ref_id_)
INTO receipt_ref_no_;INSERT INTO svk_apt_receipts
(
receipt_ref_no, paid, payable, is_paid, master_receipt_to_id, receipt_from_id, receipt_to_id, receipt_date,
receipt_mode, transaction_ref_no, customer_id, association_id, is_active, created_by, created_on, receipt_status_id, remarks
)
VALUES
( receipt_ref_no_, _total_amount, 0,1,3, receipt_from_id_, receipt_to_id_, Cast(Now()AS DATE), 3, _transaction_ref_no,
customer_id_, association_id_, 1, _created_by, Now(), 2, 'Paid through Payment Gateway'
);
As mentioned in comments, just store an autoincrementing id. All the other stuff can be handled by trivial queries and/or your presentation layer.
By way of example...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,user INT NOT NULL
);
INSERT INTO my_table (user) VALUES
(1),(1),(3),(3),(5),(2),(1),(8),(4),(5),(7),(5),(5),(4),(1),(2),(3),(6),(4),(6),(1),(5),(1),(8);
SELECT * FROM my_table;
+----+------+
| id | user |
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 3 |
| 4 | 3 |
| 5 | 5 |
| 6 | 2 |
| 7 | 1 |
| 8 | 8 |
| 9 | 4 |
| 10 | 5 |
| 11 | 7 |
| 12 | 5 |
| 13 | 5 |
| 14 | 4 |
| 15 | 1 |
| 16 | 2 |
| 17 | 3 |
| 18 | 6 |
| 19 | 4 |
| 20 | 6 |
| 21 | 1 |
| 22 | 5 |
| 23 | 1 |
| 24 | 8 |
+----+------+
24 rows in set (0.01 sec)
SELECT x.*
, COUNT(*) complaint
FROM my_table x
JOIN my_table y
ON y.user = x.user
AND y.id <= x.id
GROUP
BY x.id
ORDER
BY user
, id;
+----+------+-----------+
| id | user | complaint |
+----+------+-----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 7 | 1 | 3 |
| 15 | 1 | 4 |
| 21 | 1 | 5 |
| 23 | 1 | 6 |
| 6 | 2 | 1 |
| 16 | 2 | 2 |
| 3 | 3 | 1 |
| 4 | 3 | 2 |
| 17 | 3 | 3 |
| 9 | 4 | 1 |
| 14 | 4 | 2 |
| 19 | 4 | 3 |
| 5 | 5 | 1 |
| 10 | 5 | 2 |
| 12 | 5 | 3 |
| 13 | 5 | 4 |
| 22 | 5 | 5 |
| 18 | 6 | 1 |
| 20 | 6 | 2 |
| 11 | 7 | 1 |
| 8 | 8 | 1 |
| 24 | 8 | 2 |
+----+------+-----------+
24 rows in set (0.00 sec)

Mysql select distinct id's and select first row only

This is my table. I want to select distinct 'bill_no' with only first related row.
+--------------+--------------+-------+------------+
| id | bill_no | product_name | tax | total |
+--------------+-------+------+-------+------------+
| 1 | 12 | Hairgel | 10 | 241 |
| 2 | 12 | Spiker gel | 10 | 300 |
| 3 | 13 | Wokzem amba | 12 | 450 |
| 4 | 13 | test prod | 1 | 145 |
| 5 | 14 | wokk | 3 | 55 |
| 6 | 14 | Kamer hyp | 11 | 46 |
| 7 | 15 | Xyombokx | 2 | 220 |
+--------------+-------+------+-------+------------+
I want data to be displayed like the below table having only distinct "bill_no" -
Output-
+--------------+--------------+-------+------------+
| id | bill_no | product_name | tax | total |
+--------------+-------+------+-------+------------+
| 1 | 12 | Hairgel | 10 | 241 |
| 3 | 13 | Wokzem amba | 12 | 450 |
| 5 | 14 | wokk | 3 | 55 |
| 7 | 15 | Xyombokx | 2 | 220 |
+--------------+-------+------+-------+------------+
Use group by
select * from youtable group by bill_no
select t1.*
from your_table t1
join
(
select min(id) as id
from your_table
group by bill_no
) t2 on t1.id = t2.id
You can use GROUP BY clause.GROUP BY clause always return first row with related group by cloumn name.
SELECT * FROM `table_1` group by `bill_no`

mysql select random users but more chances for those with lower votes

Hello can someone help me with a query like i said in title? Atm i dont have any ideea how to make that. What i want is when i press on a button to display users random but those with lower votes to have more weight in the random sql.
here is my actual sql
$query = "select * from `users` where username!='$user' ".$conditie_categ." order by RAND() limit 1 ";
$result = mysqli_query($dbc,$query)
or die("query failed: " . mysqli_error($dbc));
Thnx in advance.
Crudely...
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT i
, RAND()/(i+1) wght
FROM ints
ORDER
BY wght DESC;
+---+-----------------------+
| i | wght |
+---+-----------------------+
| 1 | 0.4722126251759125 |
| 0 | 0.3937836097495478 |
| 3 | 0.217650386474701 |
| 2 | 0.18025848441470893 |
| 4 | 0.14613628009905694 |
| 7 | 0.11937898478412906 |
| 8 | 0.08080622240562157 |
| 9 | 0.07711844041675184 |
| 5 | 0.006933732585609331 |
| 6 | 0.0022811152594387277 |
+---+-----------------------+
Lower values tend to appear towards the top of the list.

SELECT records WHERE rows have difference on a specific column

I have a database table campaign_data. I need to select the customer_id where in the campaign there is difference in tariff. How can i do that with MySQL query. Here is some sample data.
SQL Fiddle Schema
| CAMPAIGN_ID | CUSTOMER_ID | CAMPAIGN_NAME | TARIFF |
---------------------------------------------------------
| 1 | 1 | Richmond | 100 |
| 2 | 1 | Sutton Coldfield | 75 |
| 3 | 1 | Putney | 100 |
| 4 | 1 | Kentish Town | 100 |
| 5 | 1 | Woking | 100 |
| 6 | 2 | Chiswick | 90 |
| 7 | 2 | Ealing | 100 |
| 8 | 2 | Camden | 100 |
| 9 | 3 | Croydon | 75 |
| 10 | 3 | Croydon1 | 100 |
| 11 | 3 | Archway | 100 |
| 12 | 4 | Ealing0 | 100 |
| 13 | 4 | Ealing01 | 100 |
| 14 | 4 | Ealing02 | 100 |
| 15 | 4 | Chingford | 100 |
| 16 | 4 | chingford01 | 100 |
Now as you can see customer id 1 , and 3 has different tariffs. I want to select them and leave the customer id 4 because it has campaigns with same tariffs.
Desired Output
| CUSTOMER_ID |
---------------
| 1 |
| 2 |
| 3 |
For clearification you can see customer 1 has 5 records. If in his 5 records the tariff is same (100) i want to avoid but if the tariff is not some as 4 records have 100 and one has 75, i want to select.
SELECT customer_id, count(DISTINCT tariff) as tariffs
FROM campaign_data
GROUP BY customer_id
HAVING tariffs > 1
you looking for this maybe
SELECT customer_id
FROM campaign_data
GROUP BY customer_id
HAVING count(DISTINCT tariff) > 1
http://sqlfiddle.com/#!2/48b6e/31
select
customer_id,
tariff
from campaign_data
group by customer_id
having sum(tariff)/count(tariff) <> tariff;

Categories