Simple mySQL subqueries - mysql

I have a simple problem, but I am new to SQL so please forgive my ignorance.
I have a accounting report that figures out balances oweing and adds up the balances to report to me what the total outstanding are within a specific period. The issue is for every JOB there are many invoices that provide a running total/balance, because of this when my current query adds up the balances it shows me outstanding amounts that are sky high, we have found that the current code is adding the balances of all the invoices.
Example-
If JOB ID 001 has four invoices-
I-001 balance 200,
I-002 balance 100,
I-003 balance 50,
I-004 balance 0.
It will show me that there is $350 outstanding when in fact it is zero.
The solution that I can think of(which I am not sure how to code) are to group the results by job ID and use the MAX feature to select only the higest ID for every JOBID
The problem I am having is that the balances are not saved to the table but recalculated every time they are needed. What can you suggest to show me only the balance from the highest Invoice ID for a particular JOBID
My invoice table has the following columns:
1 ID int(11)
2 ParentID int(11)
3 JOBID varchar(100)
4 DATE date
5 LENSES decimal(10,2)
6 FRAMES decimal(10,2)
7 TAXABLEGOODS decimal(10,2)
8 DISCOUNT decimal(10,2)
9 PREVIOUSBALANCE decimal(10,2)
10 PAYMENT decimal(10,2)
11 PAYMENTTYPE varchar(200)
12 NOTES varchar(255)
13 PMA decimal(10,2)
The current code looks like this:
$pieces = explode("-", $_REQUEST["STARTDATE"]);
$startDate=$pieces[2] . "-" . $pieces[1] . "-" . $pieces[0];
if($_REQUEST["ENDDATE"]==""){
$endDate=0;
}else{
$pieces = explode("-", $_REQUEST["ENDDATE"]);
$endDate = $pieces[2] . "-" . $pieces[1] . "-" . $pieces[0];
}
$result = mysql_query("SELECT * FROM INVOICES WHERE DATE BETWEEN '" . $startDate . "' AND '" . $endDate . "'");
$totalCount = 0;
$total = 0;
$allPayments= 0;
$pmtTypes = Array();
$totalHST = 0;
$outstanding=0;
$payments=0;
while($theRow=mysql_fetch_array($result)){
$allPayments += $theRow["PAYMENT"];
if($theRow["PAYMENTTYPE"] == "") $theRow["PAYMENTTYPE"] = "BLANK";
if(isset($pmtTypes[$theRow["PAYMENTTYPE"]])){
$pmtTypes[$theRow["PAYMENTTYPE"]] += $theRow["PAYMENT"];;
}else{
$pmtTypes[$theRow["PAYMENTTYPE"]] = $theRow["PAYMENT"];;
}
if($theRow["PREVIOUSBALANCE"] != 0) continue;
$subTotal = ( ( $theRow["LENSES"] + $theRow["FRAMES"] + $theRow["TAXABLEGOODS"] ) - $theRow["DISCOUNT"]);
$HST = ( $theRow["TAXABLEGOODS"] * 0.13 );
$totalHST+= $HST;
$total += ( $subTotal + $HST );
$payments+=$theRow["PAYMENT"];
}
$outstanding=$total-$payments;
Anyone have anything to contribute?
I would appreciate any help.

show me only the balance from the highest Invoice ID for a particular JOBID
For a single job ID:
SELECT lenses+frames+taxablegoods-discount+previousbalance AS balance
FROM invoices WHERE jobid=?
ORDER BY id DESC LIMIT 1
group the results by job ID and use the MAX feature to select only the higest ID for every JOBID
If you want to query the latest invoice for many jobs at once, you are talking about a per-group-maximum selection. SQL doesn't make this as easy to do as you'd hope. There are various approaches, including subqueries, but on MySQL I typically favour the null-self-join:
SELECT i0.jobid, i0.lenses+...etc... AS balance
FROM invoices AS i0 LEFT JOIN invoices AS i1 ON i1.jobib=i0.jobid AND i1.id>i0.id
WHERE i1.id IS NULL
That is: “give me rows where there is no row with the same job ID but a higher invoice ID”.
If doing this between two dates, you'd need to apply the condition to both sides of the join:
SELECT i0.jobid, i0.lenses+...etc... AS balance
FROM invoices AS i0 LEFT JOIN invoices AS i1 ON
i1.jobib=i0.jobid AND i1.id>i0.id AND
i1.date BETWEEN ? AND ?
WHERE
i0.date BETWEEN ? AND ?
i1.id IS NULL
Incidentally you have an SQL-injection vulnerability from putting strings into your query. Use mysql_real_escape_string() or, better, parameterised queries to avoid these problems.

The subquery would look something like this:
SELECT * FROM INVOICES I1
WHERE DATE BETWEEN ? AND ?
AND ID = (SELECT MAX(ID) FROM INVOICES I2
WHERE DATE BETWEEN ? AND ?
AND I2.JOBID = I1.JOBID)
You should look into using parameterised queries, instead of concatenating the string with user input. At a bare minimum use mysql_real_escape_string - see here: http://www.php.net/manual/en/function.mysql-real-escape-string.php

Related

Multiple conditions in mySQL

I want to filter the products on the basis of price, brand, and category. But none of them is required. So the user can filter the products on the basis of only price or price and brand. How to write this in mySQL using queries only. I have searched a lot but found the solution with the help of Stored Procedures.
If I write this query select * from product where brandid = 1 AND price = 10 and categoryid = 5. it will fetch 2 products which satisfy the where clause.
But if user doesn't want to filter the product on the basis of brand (lets say), then what will b the query? select * from product where brandid = null AND price = 10 and categoryid = 5... this will not work as I dont want to search products with brandid null. What I want is to remove that particular clause from where condition. So what my expected query is select * from product where price = 10 and categoryid = 5
Construct the query incrementally. Here it is in Ruby (since you didn't tag a programming language), but the logic is quite language-independent.
query = "SELECT * FROM products"
filters = []
params = []
if price_min
filters << "price >= ?"
params << price_min
end
if price_max
filters << "price <= ?"
params << price_max
end
if brand
filters << "brand = ?"
params << brand
end
# ...
unless filters.empty?
query += " WHERE " + filters.join(' AND ')
end

database query to get lowest price based on last crawel date

I would like to get lowest price of product based on last crawled dates by various resellers. My current function is very basic, it gets me lowest price from table without considering reseller ids and crawled timestamps.
I've rough idea that we can SELECT * FROM "custom_data_table" and process the data using php. Please have a look at attachment for further clarification.
function get_lowest_price($table_id) {
global $wpdb;
$table_prices = $wpdb->get_results(
$wpdb->prepare(
"SELECT price FROM `custom_data_table` WHERE tableid= %d"
,$table_id)
);
if (!empty($table_prices) && $table_prices !== NULL)
return rtrim(min($table_prices)->price, '00');
}
The right query here is:
SELECT price
FROM custom_data_name cdn, (
SELECT MAX(crawled) AS maxCrawled, resellerid
FROM custom_data_name
GROUP BY resellerid
) cdnFiltered
WHERE cdn.crawled = cdnFiltered.maxCrawled AND
cdn.resellerid = cdnFiltered.resellerid AND
tableid = %d;
Try this:
SELECT B.price
FROM (SELECT resellerid, MAX(crawled) max_crawled
FROM custom_data_table
GROUP BY resellerid) A
JOIN custom_data_table B
ON A.resellerid=B.resellerid AND A.max_crawled=B.crawled;
Maybe use ORDER BY crawled and LIMIT 1

mysql select count(column) where sum(column) > value

I'm trying to query $wpdb to get back an int value of the number of users in a custom table who have recorded a number of hours volunteer work above a set target - these hours need to have been moderated ( value set to = 1 ) - I have this so far:
EDIT - updated to use consistent {} around php variables in query --
$target = get_post_meta($post->ID, 'target', true) ? (int)get_post_meta($post->ID, 'target', true) : 100;
$awards = $wpdb->get_var("
SELECT user_id
FROM {$this->options['rewards_logging']}
WHERE moderated = 1 AND reward_id = {$post->ID}
GROUP BY user_id
HAVING sum(hours) > {$target}
");
Which returns the correct value of '0' if none of the hours are approved ( moderated = 0 ), however as soon as one of those users hours are approved, this query returns the count of all the users who have logged more than the target hours ( whether they have been approved or not ).
Any pointers!
Cheers
Ray
Seems I was trying to get back a single variable using $wpdb->get_var, when I really needed the whole result set:
$awards = $wpdb->get_results("
SELECT user_id
FROM {$this->options['rewards_logging']}
WHERE moderated = 1 AND reward_id = {$post->ID}
GROUP BY user_id
HAVING sum(hours) > {$target}
");
Then I can check over the data and display a result - etc...:
if ( count($awards) > 0 ) {
#var_dump($awards);
echo '<span class="awards-notice">'.count($awards).'</span>';
} else {
echo '-';
}

MySQL SUM() Operatiion

I have a MySQL table payment where I store all the payment related data of my client. The table fields are: fileNo, clientName, billNo, billAmount, status. I want to build a search form where I will input the fileNo or the clientName, which will yield a table fetching all the records for that file number or the client name where status = 0 (unpaid). Here billAmount is a floating point number.
I am no good in MySQL, but here is my version of the SQL by fileNo
$sql = "SELECT * FROM `payment` WHERE `fileNo` = '$fileNo' AND `status` = '0'";
SQL by clientName
$sql = "SELECT * FROM `payment` WHERE `clientName` = '$clientName' AND `status` = '0'";
either way I make the query, I will also need to show the total unpaid amount against that fileNo or clientName.
From my understanding, the SUM() operation should be something like this:
$sql = "SELECT SUM(billAmount) AS `unpaid` WHERE `fileNo` = '$fileNo' AND `status` = '0'";
or
$sql = "SELECT SUM(billAmount) AS `unpaid` WHERE `clientName` = '$clientName' AND `status` = '0'";
My question is, am I correct with my SUM() operation? And how do I get the total unpaid amount that I selected as unpaid? can I store it in a variable?
Any idea, suggestions or resource link will be much appreciated!
i think u should use the second query but u missed WHEre clause
$sql = "SELECT SUM(billAmount) AS `unpaid` FROM payment WHERE `clientName` = '$clientName' AND `status` = '0'";
your total amount unpaid yes u cant get it as variable like that
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
ehco $row['unpaid'] ; <------ this will give you the unpaid amount.
of course after u fetch your query .
I think your query is quite right, don't forget the FROM clause :
$sql = "SELECT SUM(billAmount) AS `unpaid` FROM payment WHERE `clientName` = '$clientName' AND `status` = '0'";
Be careful if your client's name possibly contains quotes (have a look on addslashes function)
Then, to get the sum :
$sum = current(mysql_fetch_row(mysql_query($sql)));
BTW
In this query you have missed out FROM Clause :-)
"SELECT SUM(billAmount) AS `unpaid`
FROM payment
WHERE `fileNo` = '$fileNo'
AND `clientName` = '$clientName'
AND `status` = '0'";
Yes it is possible to store a value in a variable from first query and to be used in a secondaries.
I wish if I could give you a proper sample from SQLFIDDLE, on mobile now. Will update later if you can try this code.

MYSQL inventory slots (add item to next slot free)

$slt = mysql_query("select Slot, ItemId, UserId, max(Slot) Slot
from useritems
group by UserId");
while ($sloot = mysql_fetch_assoc($slt))
{
Echo "<br>Items with biggest slots are: " . $sloot['ItemId'] . " from user " . $sloot['UserId']. "- in slot-". $sloot['Slot'];
}
This is the table
Idi Quantity ItemId UserId Slot ExpirationDate
Outputs the smallest Slots...Why?
1.I want to show me the biggest inventory slot from each user, so when i add a new item to his inventory i can add on next slot..From example user Paul has 5 items that ocupies slots 1,2,3,4,5 and the next item will be on slot 6.
2.When a user moves his items on slots 1,2,4,5,6 the next item added will be on slot 3
I did a lot of search but i can't find out myself:) PS:The game wich im making its just for fun..but maybe someday will be a great game:) (dreams,dreams :)) )
EDIT:
SQLFIDDLE is very good thank you:) it's exactly what i need to learn some SQL
Table useritems
useritems Table IMAGE
items Table
My Echo shows me that:
Id = 1 and it should be 3; user=4(good); slot=4(good)
Id = 1 and it should be 2; user=5(good); slot=2(good)
Maybe something like:
select userid, itemid, max(slot) from useritems where itemid is not null and quantity>0
group by userid, itemid
It'd be easier to help you if you share your table script with some data.
You can use: http://sqlfiddle.com/
EDIT:
What about this? Itemid=0 means the slot is free? So min(slot) will be the first free slot by user.
select userid, min(slot) from useritems where itemid=0 group by userid
I found it!:) i just forgot to select data where UserId=my_id but it shows me the corect output only if the respective user has more than 3 items...
$slt = mysql_query("SELECT * FROM useritems WHERE Slot=(select max(Slot) from useritems) and UserId='$id'");
and $id = $_SESSION['id'];
$slt = mysql_query("
SELECT * FROM `useritems`
WHERE UserId='$id' AND Slot=(select max(Slot)
from useritems where UserId='$id')
");`
EDIT2:I found the best way with all i was searching but i don`t knwo how to use WHERE clause userId='$id'
$slt2 = mysql_query("select l.Slot + 1 as start
from useritems as l
left outer join useritems as r on l.Slot + 1 = r.Slot
where r.Slot is null and l.UserId=4;
") or die(mysql_error()); `
With this query the item should be placed on Slot 2(wich is missing) but it`s dysplays Slot 6 (wich is the highest for the user with UserId=4)
Finally this is the last edit
$slt2 = mysql_query("SELECT Slot + 1 as start
FROM useritems mo
WHERE NOT EXISTS
(
SELECT NULL
FROM useritems mi
WHERE mi.Slot = mo.Slot + 1 AND mi.UserId = '$id'
)ORDER BY Slot LIMIT 1
") or die(mysql_error()); `
This is what i was searching.:X