How to fix problem with sum, length and group by - mysql

Field contracts is a text, value is strings separated by "," .
A query:
select sum( if( length(dapps.contracts) =
sum(length(replace(dapps.contracts,',',''))) , 1 , length(dapps.contracts)
- sum(length(replace(dapps.contracts,',',''))) ) ) as f1
from dapps
group by id
show :
Error in query (1111): Invalid use of group function
Need a one level query
This query work fine
select sum(f1)
from (
select if( length(dapps.contracts) =
sum(length(replace(dapps.contracts,',',''))) , 1 , length(dapps.contracts)
- sum(length(replace(dapps.contracts,',',''))) ) as f1
from dapps
group by id
) tb1​
Need query without subquery

Related

Mysql Stored procedure returns some weird results

When I run the following select Query in SQl
SELECT Count(*)
FROM workordercurrent
WHERE office_id = 1
AND ( ( scheduleddate = '2018-11-01' )
OR ( schedulestopdate = '2018-11-01' )
OR ( scheduleddate = '0000-00-00'
AND orderdate = '2018-11-01' ) )
AND worktype <> 6
The query returns 694 as count which is right
When I write the same Query in the SQL Procedure with 2 input parameters
office_id(int) and order_date (DATE)
BEGIN
SELECT Count(*)
FROM workordercurrent
WHERE office_id = office_id
AND ( ( scheduleddate = order_date )
OR ( schedulestopdate = order_date )
OR ( scheduleddate = '0000-00-00'
AND orderdate = order_date ) )
AND worktype <> 6;
END
It returns the count as 3260
What is the problem here as both queries exactly same. Here is how I am running the Stored procedure
You should avoid using Stored procedure's parameter name same as the columns/aliases used in your SP. WHERE office_id = office_id is behaving weird due to ambiguous name. MySQL is probably not able to resolve it as either a column name or parameter.
I normally prefix in_ or out_ or inout_ to param names; which also shows the type of param (for readability).
So you can rename the parameters to in_office_id and in_order_date instead.

MySQL Query to average 3 columns and exclude 0's?

This is obviously wrong, but what would be the correct way to average the SUM of 3 columns and exclude the 0's?
SELECT (
AVG(NULLIF(`dices`.`Die1`,0)) +
AVG(NULLIF(`dices`.`Die2`,0)) +
AVG(NULLIF(`dices`.`Die3`,0))
) /3 as avgAllDice
FROM (
SELECT `Die1`,`Die2`,`Die3` FROM `GameLog`
WHERE PlayerId = "12345"
) dices
Thanks.
If I was keeping the inline view query (it's not clear why it's needed). I'd probably do something like this:
SELECT AVG( NULLIF( CASE d.i
WHEN 1 THEN dices.`Die1`
WHEN 2 THEN dices.`Die2`
WHEN 3 THEN dices.`Die3`
END
,0)
) AS `avgAllDice`
FROM ( SELECT gl.`Die1`
, gl.`Die2`
, gl.`Die3`
FROM `GameLog` gl
WHERE gl.playerId = '12345'
) dices
CROSS
JOIN ( SELECT 1 AS i UNION ALL SELECT 2 UNION ALL SELECT 3 ) d
The trick is the cross join operation, giving me three rows for each row returned from dices, and an expression that picks out values of Die1, Die2 and Die3 on each of three rows, respectively.
To exclude values of 0, we replace 0 with with NULL (since AVG doesn't include NULL values.)
Now with all of the non-zero DieN values stacked into a single column, we can just use the AVG function.
Another way to do it would be to get the numerator and denominator for each of Die1, Die2, Die3.... and then total up the numerators, total up the denominators, and then divide the total numerator by the total denominator.
This will should give an equivalent result.
SELECT ( IFNULL(t.n_die1,0) + IFNULL(t.n_die2,0) + IFNULL(t.n_die3,0) )
/ ( t.d_die1 + t.d_die2 + t.d_die3 )
AS avgAllDice
FROM ( SELECT SUM( NULLIF(gl.die1,0)) AS n_die1
, COUNT(NULLIF(gl.die1,0)) AS d_die1
, SUM( NULLIF(gl.die2,0)) AS n_die2
, COUNT(NULLIF(gl.die2,0)) AS d_die2
, SUM( NULLIF(gl.die3,0)) AS n_die3
, COUNT(NULLIF(gl.die3,0)) AS d_die3
FROM `GameLog` gl
WHERE gl.playerid = '12345'
) t
(I didn't work out what gets returned in the edge and corner cases... no matching rows in GameLog, all values of Die1, Die2 and Die3 are zero, etc., for either query. The results might be slightly different, returning a zero instead of NULL, divide by zero edge case, etc.)
FOLLOWUP
I ran a quick test of both queries.
CREATE DATABASE d20170228 ;
USE d20170228 ;
CREATE TABLE GameLog
( playerid VARCHAR(5) DEFAULT '12345'
, die1 TINYINT
, die2 TINYINT
, die3 TINYINT
);
INSERT INTO GameLog (die1,die2,die3)
VALUES (3,0,0),(2,1,0),(4,3,3),(3,3,3),(0,0,0),(4,4,4),(5,4,0),(0,0,2)
;
SELECT (3+2+1+4+3+3+3+3+3+4+4+4+5+4+2)/15 AS manual_avg
manual_avg is coming out 3.2.
Both queries are also returning 3.2
If you want to eliminate zeroes and NULLs, you can simply SELECT from the filtered master set multiple times, doing a UNION ALL on the results, then averaging against that.
SELECT AVG(`allDice`.`DieResult`)
FROM (
SELECT `Die1` AS `DieResult` FROM `GameLog` WHERE COALESCE(`Die1`, 0) <> 0 AND PlayerId = '12345'
UNION ALL
SELECT `Die2` FROM `GameLog` WHERE COALESCE(`Die2`, 0) <> 0 AND PlayerId = '12345'
UNION ALL
SELECT `Die3` FROM `GameLog` WHERE COALESCE(`Die3`, 0) <> 0 AND PlayerId = '12345'
) AS `allDice`
There's no need to overthink this one, it's not too difficult a problem

run a query if another query fetches null value in mysql

I want to fetch max no from invgatepass on the basis of time and date and if it returns null then I want to run another query on the basis of different conditions if that also fails to fetch any data then I want to fetch '1'.
This is something which I want.
But inside COALESCE function, second parameter is not allowed to be query.
Can some one solve this problem.
I don't want to use two separate queries after checking the condition inside php code.
SELECT COALESCE(
MAX(SUBSTRING_INDEX(InwardNo, '-', -1))+1,
SELECT COALESCE
(
MAX(SUBSTRING_INDEX(InwardNo, '-', -1))+1,
1
)
FROM
invgatepass
WHERE
DATE(CreationDateTime)=CURDATE()
AND
(EXTRACT(HOUR FROM CreationDateTime)) < '17'
)
AS CODE FROM
invgatepass
WHERE
DATE(CreationDateTime)=DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND
(EXTRACT(HOUR FROM CreationDateTime)) >= '17'"
A subquery is allowed inside the coalesce function, but the subquery must be enclosed within braces.
Look at this demo: http://www.sqlfiddle.com/#!2/fb3d8/2
This query compiles fine because all subqueries are enclosed within braces:
SELECT coalesce(
( SELECT null ),
( SELECT id FROM test WHERE id = 10 ),
( SELECT id FROM test WHERE id = 4 ),
( SELECT id FROM test WHERE id = 2 )
) result
;
This query throws a syntax error, because a second subquery has no braces around it:
SELECT coalesce(
( SELECT null ),
SELECT id FROM test WHERE id = 10,
( SELECT id FROM test WHERE id = 4 ),
( SELECT id FROM test WHERE id = 2 )
) result
;
Side note:
You do not need 2 Coalesce:
coalesce ( null, null, 1)
is about the same as
coalesce ( null, coalesce ( null, 1))

MySQL - query to return NULL

I have the following code:
SELECT q25, (
(
AVG( q1 ) + AVG( q2 ) + AVG( q3 ) ) /3 ) AS Overall
FROM t_results
WHERE brand = 'XYZ'
AND DATE = 'MAY2012'
GROUP BY q25
ORDER BY Overall
DESC LIMIT 1
If there is no data found by the query phpmyadmin returns the following message (which is quite correct):
MySQL returned an empty result set (i.e. zero rows). ( Query took 0.0178 sec )
However, what I'd like is to actually return a NULL value, is this possible? I appreciate this might not be best practise but I'm working with inherited code and this might be the simplist and quickest route to a solution.
Thanks as always,
H.
Create a table with exactly one row. Then you can use left join to achieve the desired NULL result.
CREATE TABLE dummy (d TINYINT NOT NULL);
INSERT INTO dummy SET d = 1;
SELECT q25,
( ( AVG( q1 ) + AVG( q2 ) + AVG( q3 ) ) /3 ) AS Overall
FROM dummy LEFT JOIN t_results
ON brand = 'XYZ'
AND DATE = 'MAY2012'
GROUP BY q25
ORDER BY Overall DESC
LIMIT 1
You can also replace the dummy table with a subquery:
SELECT q25,
( ( AVG( q1 ) + AVG( q2 ) + AVG( q3 ) ) /3 ) AS Overall
FROM (SELECT 1) AS dummy LEFT JOIN t_results
ON brand = 'XYZ'
AND DATE = 'MAY2012'
GROUP BY q25
ORDER BY Overall DESC
LIMIT 1
Tested this via sqlfiddle, where you can also experiment with alternatives.
The conditions selecting the result, which used to be in the WHERE clause, now have to go into the ON clause. Otherwise the left join would produce non-NULL rows which would be removed by the WHERE, instead of generating a single NULL row if no matching row could be found. If there were no WHERE conditions in the original query, ON 1 could be used to express any row matches.
You can use a UNION combined with a LIMIT to supply the NULL values:
(SELECT q25,
(AVG(q1) + AVG(q2) + AVG(q3))/3 AS Overall
FROM t_results
WHERE brand = 'XYZ'
AND DATE = 'MAY2012'
GROUP BY q25
ORDER BY Overall DESC
LIMIT 1
)
UNION ALL
(SELECT NULL, NULL)
LIMIT 1;
This only works when you know that the first query will never yield more than one result, though. Which is the case here, so this might be the best solution for you, but the approach given in my other answer is more general.
There is a fiddle for this to experiment with.
The coalesce() function can be used to return the first non-null value from a number of comma separated columns or strings. The values/columns are evaluated left to right, so if you want to pop a string into the arguments that isn't null, make sure you place it to the right of the columns that you are testing against.
select
coalesce(
(
SELECT
q25
FROM
t_results
WHERE
brand = 'XYZ'
AND DATE = 'MAY2012'
GROUP BY
q25
LIMIT 1
), 'null') as q25,
coalesce(
(
SELECT
((AVG( q1 ) + AVG( q2 ) + AVG( q3 ) ) /3 ) AS Overall
FROM t_results
WHERE
brand = 'XYZ'
AND DATE = 'MAY2012'
LIMIT 1
), 'null') as Overall
from
t_results
group by
1, 2;
If you don't have data that matches your where clause, this will return null, null as a row.

MySQL Join Two Queries Horizontally

I have a query that works correctly to pull a series of targets and total hours worked for company A. I would like to run the exact same query for company B and join them on a common date, which happens to be grouped by week. My current query:
SELECT * FROM (
SELECT org, date,
( SELECT SUM( target ) FROM target WHERE org = "companyA" ) AS companyA_target,
SUM( hours ) AS companyA_actual
FROM time_management_system
WHERE org = "companyA"
GROUP BY WEEK( date )
ORDER BY DATE
) q1
LEFT JOIN (
SELECT org, date,
( SELECT SUM( target ) FROM target WHERE org = "companyB" ) AS companyB_target,
SUM( hours ) AS companyB_actual
FROM time_management_system
WHERE org = "companyB"
GROUP BY WEEK( date )
ORDER BY DATE
) q2
ON q1.date = q2.date
The results show all of the dates / information of companyA, however companyB only shows sporadic data. Separately, the two queries will show the exact same set of dates, just with different information in the 'target' and 'actual' columns.
companyA 2012-01-28 105.00 39.00 NULL NULL NULL NULL
companyA 2012-02-05 105.00 15.00 NULL NULL NULL NULL
companyA 2012-02-13 105.00 60.50 companyB 2012-02-13 97.50 117.50
Any idea why I'm not getting all the information for companyB?
As a side note, would anybody be able to point in the direction of converting each row's week value into a column? With companyA and companyB as the only two rows?
I appreciate all the help! Thanks.
WITH no date apparent in the target table, the summation will be constant across all weeks. So, I have performed a pre-query for only those "org" values of company A and B with a group by. This will ensure only 1 record per "org" so you don't get a Cartesian result.
Then, I am querying the time_management_system ONCE for BOTH companies. Within the field computations, I am applying an IF() to test the company value and apply when correct. The WEEK activity is the same for both in the final result, so I don't have to do separately and join. This also prevents the need of having the date column appear twice. I also don't need to explicitly add the org column names as the final column names reflect that.
SELECT
WEEK( tms.date ) as GrpWeek,
IF( tms.org = "companyA", TargetSum.CompTarget, 00000.00 )) as CompanyATarget,
SUM( IF( tms.org = "companyA", tms.hours, 0000.00 )) as CompanyAHours,
IF( tms.org = "companyB", TargetSum.CompTarget, 00000.00 )) as CompanyBTarget,
SUM( IF( tms.org = "companyB", tms.hours, 000.00 )) as CompanyBHours
from
Time_Management_System tms
JOIN ( select
t.org,
SUM( t.target ) as CompTarget
from
Target T
where
t.org in ( "companyA", "companyB" )
group by
t.org ) as TargetSums
ON tms.org = TargetSums.org
where
tms.org in ( "companyA", "companyB" )
group by
WEEK( tms.date )
order by
WEEK( tms.date )
Both of your subqueries are wrong.
Either you want this:
SELECT
org,
WEEK(date),
( SELECT SUM( target ) FROM target WHERE org = "companyB" ) AS companyB_target,
SUM( hours ) AS companyB_actual
FROM time_management_system
WHERE org = "companyB"
GROUP BY WEEK( date )
Or else you want this:
SELECT
org,
date,
( SELECT SUM( target ) FROM target WHERE org = "companyB" ) AS companyB_target,
SUM( hours ) AS companyB_actual
FROM time_management_system
WHERE org = "companyB"
GROUP BY date
The way you are doing it now is not correctly formed SQL. In pretty much any other database your query would fail immediately with an error. MySQL is more lax and runs the query but gives indeterminate results.
GROUP BY and HAVING with Hidden Columns