SQL Query, Subquery won't return data - mysql

Problem: My Query returns NULL
Potential issues: Subquery formatted wrong and only works if some data I want to sort out is in the dataset.
Example of today's code:
SELECT production_order
,SUM(total_working_time_h) - (SELECT SUM(total_working_time_h) FROM {$table} WHERE production_order = '$production_order' AND (station = '出货检验 | OQC' OR production_type = 'Rework')) AS total_working_time_h_edit
,SUM(no_of_defects) AS no_of_defects_during_production
FROM {$table}
WHERE production_order = '$production_order'
This works great as long as I have either "Rework" and/or "'出货检验 | OQC'" logged in my database for this production order. If not, I won't get any data at all but NULL. So my problem is somewhere in my subquery I think:
SELECT SUM(total_working_time_h) FROM {$table} WHERE production_order = '$production_order' AND (station = '出货检验 | OQC' OR production_type = 'Rework')
I've tried adding a "0" to make sure it's get 0 and not NULL without success like follows:
(0 + SELECT SUM(total_working_time_h) FROM {$table} WHERE production_order = '$production_order' AND (station = '出货检验 | OQC' OR production_type = 'Rework')
Example dataset formated as text:
production_order part_nr station total_working_time_h
26135 129-108816B-UL 压接 | Crimping 1.42
26135 129-108816B 线束组装 | Harness Assembling 7.67
26135 129-108816 测试 | Testing 0.83
26135 129-108816B 外观全检 | Appearance inspection 0.83
Gives this output:
production_order,total_working_time_h_edit
26135, NULL
I want this output:
production_order,total_working_time_h_edit
26135, 10,45
If my dataset hold the station I want to sort out or the production_type I want to sort out everything works as it should. But if both of those is missing in the dataset it returns NULL, since what I think is because the subquery returns 0.
An example of different datasets I'm using. If I have the data marked with green cells in the dataset it will work. If not, I get the return NULL as above.
So how to make my subquery to not return NULL?

Managed to solve the question myself.
Problem: Subquery returned NULL instead of 0.
Solution: Added COALESCE(,0) to my sub querys function SUM() like this:
COALESCE(SUM(total_working_time_h),0)
By doing so it returns 0 instead of NULL when the SUM() actually wants to return NULL. That solved my problem.

Related

Ignore empty values on average math, but show it as zero on result

I need to calculate the Average value of fields, but two things needs to happen:
1- The empty values should NOT be counted for the average math.
2- If the field is empty it still must be shown in the result (with avg === 0)
Imagine that I have this dataset:
-----------------------
Code | valField | Date
-----------------------
A | | 2020-09-08
B | 12 | 2020-09-09
A | 10 | 2020-09-08
B | 15 | 2020-09-09
B | | 2020-09-09
C | | 2020-09-09
So I need the average of the day. As you can see, we have:
A = { empty, 10 }
B = { 12, 15, empty }
C = { empty }
I need to make the average like this:
Average of A = 10
Average of B = (12+15)/2 (because we have 2 non-empty values)
Average of C = 0 (It has not a single value, but I need it to show on result as 0)
So far I could accomplish both of the requirements, but not in the same time.
This query will show empty values BUT will also count empty fields on average math
SELECT AVG(valField) FROM myTable;
So Average of B would be = (12+15+0)/3 - wrong!
Now this will ignore empty values, the AVG math will be correct, but C would NOT be shown.
SELECT AVG(valFIeld) FROM myTable WHERE valField <> ''
How may I accomplish both requirements?
From your comment I understood, you have valField defined as varchar, so you can use next trick:
select
Code,
coalesce(avg(nullif(valField, '')), 0) as avg_value
from tbl
group by Code;
Test the query on SQLize.online
Here I used NULLIF function for convert empty values to null before calculate the average
I think you want:
SELECT code, COALESCE(AVG(valField), 0) FROM myTable GROUP BY code
This assumes valField is of a numeric datatype, and that by empty you mean null.
Here is what happens behind the hood:
avg(), as most other aggregate functions, ignores null values
if all values are null, then avg() does return null; you can replace that with 0 using coalesce()
That should be easy just create two queries one that calculates the average using non null values and the other one calculating the codes having no value in the data.
select round(avg(valField)) as avg, code from new where valField is not null group by Code
union all
select 0 as avg, code from new group by Code having avg(valField) is null;

Conditional WHERE statements based on parameters?

I am trying to conditionally create a WHERE clause in a stored procedure. It is to act as a filter on a boolean column and there's only two outcomes I want - either only take the true values, or take all of them (no situation where I only need the false values).
The clause I am trying to use is this -
WHERE
(#customerInactive = -1 OR `listcustomers`.`active` = 1)
with the idea either the parameter is a -1 (no filter) or we do have a filter and should do listcustomers.active = 1.
I tried being more explicit as well
WHERE
((#customerInactive = 1 AND `listcustomers`.`active` = 1) OR
(#customerInactive <> 1 AND 1=1))
The second one ends up not returning anything then. How can I fix this?
This is in a stored procedure, using MySQL 5.6.
Edit: Given that I've been told my first query should do it, but it always returns listcustomers.active = 1, is this possibly a type issue? I made customerInactive a int(11). I also just tried it as a bit(1) but I am still getting the same issue, no matter my paraemter I get the TRUE filter. Or in the case of the second query, no results.
Edit 2: I don't think this should matter but the final result is the union of multiple tables of which I am going to have to do this same sort of filtering. The whole SQL query can be seen here - https://pastebin.com/6wL4ZtnF
Considering your comments, this is the results you want depending of customerInactive and the value of listcustomers.active
#customerInactive | listcustomers.active | result
------------------+----------------------+--------
0 | 0 | 1
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
This is a NAND (Not And) that can be written such way in SQL :
NOT (#customerInactive = 1 AND listcustomers.active = 1)
The correct answer is that I confused parameters and user defined variables. I am passing in customerInactive as a parameter to the stored procedure, so I should reference it directly by name. Using the # made it look like a User defined variable which was NOT defined, hence the constant failing of my initial equality and why the true filter was always on. Thanks everyone.

MySql NOT IN failing to return empty set

I am currently having a problem when trying to select where a job is listed in the tbl_jobs table and has not been assigned to a delivery item in the tbl_delivery_items table by using a NOT IN subquery.
The sub query should return supplier_job_job_id 1 (which it does when you run this as a seperate query), with the NOT IN excluding the job with an id of 1. Alas, it is not working and causing me a headache by returningthe job with a job_id of 1 when I was expecting an empty set. Here is the codeigniter code generating the query:
$this->db->join("tbl_jobs", "tbl_jobs.job_id = tbl_supplier_jobs.supplier_job_job_id");
$this->db->where_not_in("supplier_job_job_id", "SELECT delivery_item_job_id FROM tbl_delivery_items");
$result = $query->result_array();
echo $this->db->last_query();
return $result;
Here is the query it generates:
SELECT * FROM (`tbl_supplier_jobs`) JOIN `tbl_jobs` ON `tbl_jobs`.`job_id` = `tbl_supplier_jobs`.`supplier_job_job_id` WHERE `supplier_job_job_id` NOT IN ('SELECT delivery_item_job_id FROM tbl_delivery_items') AND `supplier_job_supplier_id` = '1' ORDER BY `tbl_jobs`.`job_number` DESC
And here is the data:
tbl_supplier_jobs
supplier_job_id | supplier_job_job_id | supplier_job_supplier_id
1 1 1
2 2 2
tbl_jobs
job_id | job_number | job_description | job_delivered
1 1024 aaaaa 0
2 2048 bbbbb 0
tbl_delivery_items
delivery_item_id | delivery_item_delivery_id | delivery_item_job_id | delivery_item_toa | delivery_item_pallet_quantity | delivery_item_box_quantity
1 1 1 2014-08-18 16:23:04 2 1
Any ideas?
The problem is that the subquery is rendered as a string. You can see this clearly in the generated query that you supplied.
This seems to be a limitation in the where_not_in method of CodeIgniter. A possible solution, change the code to call the where method and render a slightly larger part of the query yourself:
$this->db->where("supplier_job_job_id NOT IN (SELECT delivery_item_job_id FROM tbl_delivery_items)");
The query isn't executing the subquery it is using the string value:
`supplier_job_job_id` NOT IN (
'SELECT delivery_item_job_id FROM tbl_delivery_items'
)
Will check if supplier_job_job_id equals the string 'SELECT delivery_item_job_id FROM tbl_delivery_items'.
You should consider a LEFT JOIN to tbl_delivery_items and a WHERE condition of delivery_item_job_id IS NULL.. which should be fairly easy in your framework.
Your subselect is being output as a string. Note that it is in single quotes in your resulting query. That of course will not work.
I would actually question your intended approach here. As your tbl_delivery_items table gets bigger and bigger your query will get slower and slower. This is not a scalable approach. You should revisit your table schema and get a more direct way of flagging completed deliveries.

My sql Query returning null inspite of having data in the table

I have this query which I am building in my DAO Class after taking the input from te users.Its basic a search functionality with around 10 options users provide..I wrote the below query,
Users give the range of date.
Users provide range of rent.(This part is when it goes as null,I get 0 records.How do I handle null values and still make mysql return data.)
Users select an option to see records which has images.
Status flag for this query should be 1.
Its search by Title.Which means that 1 to 4 points above should be met and keyword given by user should search the column title with like operator.
Finally results are sorted by date.
.
select SQL_CALC_FOUND_ROWS *
from `database`.`table`
WHERE (Date(ctimestamp) BETWEEN '2011-12-02' and '2012-12-06') and (crent BETWEEN '' and '')
and (cimg1 IS NOT NULL or cimg2 IS NOT NULL or cimg3 IS NOT NULL or cimg4 IS NOT NULL or cimg5 IS NOT NULL or cimg6 IS NOT NULL or cimg7 IS NOT NULL or cimg8 IS NOT NULL)
and cflag = 1 and ctitle LIKE '%Testing%'
order by Date(ctimestamp) desc Limit 0, 100
All the parameters are coming from front end.Now for this query I have close to 13 records with same parameters and conditions,but some how when I run it mysql client it gives me 0 records written.
This query gives me the data correctly,
select SQL_CALC_FOUND_ROWS *
from `database`.`table`
WHERE (Date(ctimestamp) BETWEEN '2011-12-02' and '2012-12-06')
and (cimg1 IS NOT NULL or cimg2 IS NOT NULL or cimg3 IS NOT NULL or cimg4 IS NOT NULL or cimg5 IS NOT NULL or cimg6 IS NOT NULL or cimg7 IS NOT NULL or cimg8 IS NOT NULL)
and cflag = 1 and ctitle LIKE '%Testing%'
order by Date(ctimestamp) desc Limit 0, 100
The difference between the two is this part
and (crent BETWEEN '' and '')
Now my doubt is how do I handle this situation when users plans to skip these fields.Appreciate some insights.
crent is likely a numeric value. You're asking whether it's between two text fields. That condition will never be true.
Add validation of the input parameters in your program. Do not put it in the sql when it's NULL.

How to use result of an subquery multiple times into an query

A MySQL query needs the results of a subquery in different places, like this:
SELECT COUNT(*),(SELECT hash FROM sets WHERE ID=1)
FROM sets
WHERE hash=(SELECT hash FROM sets WHERE ID=1)
and XD=2;
Is there a way to avoid the double execution of the subquery (SELECT hash FROM sets WHERE ID=1)?
The result of the subquery always returns an valid hash value.
It is important that the result of the main query also includes the HASH.
First I tried a JOIN like this:
SELECT COUNT(*), m.hash FROM sets s INNER JOIN sets AS m
WHERE s.hash=m.hash AND id=1 AND xd=2;
If XD=2 doesn't match a row, the result is:
+----------+------+
| count(*) | HASH |
+----------+------+
| 0 | NULL |
+----------+------+
Instead of something like (what I need):
+----------+------+
| count(*) | HASH |
+----------+------+
| 0 | 8115e|
+----------+------+
Any ideas? Please let me know! Thank you in advance for any help.
//Edit:
finally that query only has to count all the entries in an table which has the same hash value like the entry with ID=1 and where XD=2. If no rows matches that (this case happend if XD is set to an other number), so return 0 and simply hash value.
SELECT SUM(xd = 2), hash
FROM sets
WHERE id = 1
If id is a PRIMARY KEY (which I assume it is since your are using a single-record query against it), then you can just drop the SUM:
SELECT xd = 2 AS cnt, hash
FROM sets
WHERE id = 1
Update:
Sorry, got your task wrong.
Try this:
SELECT si.hash, COUNT(so.hash)
FROM sets si
LEFT JOIN
sets so
ON so.hash = si.hash
AND so.xd = 2
WHERE si.id = 1
I normally nest the statements like the following
SELECT Count(ResultA.Hash2) AS Hash2Count,
ResultA.Hash1
FROM (SELECT S.Hash AS Hash2,
(SELECT s2.hash
FROM sets AS s2
WHERE s2.ID = 1) AS Hash1
FROM sets AS S
WHERE S.XD = 2) AS ResultA
WHERE ResultA.Hash2 = ResultA.Hash1
GROUP BY ResultA.Hash1
(this one is hand typed and not tested but you should get the point)
Hash1 is your subquery, once its nested, you can reference it by its alias in the outer query. It makes the query a little larger but I don't see that as a biggy.
If I understand correctly what you are trying to get, query should look like this:
select count(case xd when 2 then 1 else null end case), hash from sets where id = 1 group by hash
I agree with the other answers, that the GROUP BY may be better, but to answer the question as posed, here's how to eliminate the repetition:
SELECT COUNT(*), h.hash
FROM sets, (SELECT hash FROM sets WHERE ID=1) h
WHERE sets.hash=h.hash
and sets.ID=1 and sets.XD=2;