Assistance with a complex MySQL SQL Query - mysql

I hope this is the appropriate forum to ask for assistance. I have an SQL Query (MySQL) that is not returning the correct records in a Date Range (between two dates). I am happy to answer questions in relation to the query, however if anyone can make suggestions or correct the SQL Query that would be an excellent learning exercise. Thank you.
$raw_query = sprintf("SELECT
swtickets.ticketid AS `Ticket ID`,
swtickettimetracks.tickettimetrackid AS `Track ID`,
swtickets.ticketmaskid AS `TicketMASK`,
(
SELECT
swcustomfieldvalues.fieldvalue
FROM
swcustomfieldvalues,
swcustomfields
WHERE
swcustomfieldvalues.customfieldid = swcustomfields.customfieldid
AND swtickets.ticketid = swcustomfieldvalues.typeid
AND swcustomfields.title = 'Member Company'
ORDER BY
swcustomfieldvalues.customfieldvalueid DESC
LIMIT 1
) AS MemberCompany,
(
SELECT
swcustomfieldvalues.fieldvalue
FROM
swcustomfieldvalues,
swcustomfields
WHERE
swcustomfieldvalues.customfieldid = swcustomfields.customfieldid
AND swtickets.ticketid = swcustomfieldvalues.typeid
AND swcustomfields.title = 'Member Name'
ORDER BY
swcustomfieldvalues.customfieldvalueid DESC
LIMIT 1
) AS MemberName,
(
SELECT
swcustomfieldvalues.fieldvalue
FROM
swcustomfieldvalues,
swcustomfields
WHERE
swcustomfieldvalues.customfieldid = swcustomfields.customfieldid
AND swtickets.ticketid = swcustomfieldvalues.typeid
AND swcustomfields.title = 'Chargeable'
AND
swcustomfieldvalues.fieldvalue = '40'
ORDER BY
swcustomfieldvalues.customfieldvalueid ASC
LIMIT 1
) AS `Chg`,
swtickets.`subject` AS `Subject`,
swtickets.departmenttitle AS Category,
FROM_UNIXTIME(
swtickettimetracks.workdateline
) AS `workDateline`,
FROM_UNIXTIME(
swtickettimetracks.dateline
) AS `dateline`,
swtickettimetracks.timespent AS `Time Spent`,
swtickets.timeworked AS `Time Worked`
FROM
swtickets
INNER JOIN swusers ON swtickets.userid = swusers.userid
INNER JOIN swuserorganizations ON swuserorganizations.userorganizationid = swusers.userorganizationid
INNER JOIN swtickettimetracks ON swtickettimetracks.ticketid = swtickets.ticketid
WHERE
swuserorganizations.organizationname = '%s'
AND (
swtickets.ticketstatustitle = 'Closed'
OR swtickets.ticketstatustitle = 'Completed'
)
AND FROM_UNIXTIME(`workDateline`) >= '%s' AND FROM_UNIXTIME(`workDateline`) <= '%s'
ORDER BY `Ticket ID`,`Track ID`",
$userOrganization,
$startDate,
$endDate
);
As I mentioned, the Query works - however it does not return the records correctly between the two dates.
However, IF I run this simple query against the database :
SELECT swtickettimetracks.tickettimetrackid,
swtickettimetracks.ticketid,
swtickettimetracks.dateline,
swtickettimetracks.timespent,
swtickettimetracks.timebillable,
FROM_UNIXTIME(swtickettimetracks.workdateline)
FROM swtickettimetracks
WHERE FROM_UNIXTIME(swtickettimetracks.workdateline) >= '2013-04-16' AND FROM_UNIXTIME(swtickettimetracks.workdateline) <= '2013-04-18'
I get the correct date range returned. Help? Thank you in anticipation.
Edward.

Unless you are overthinking it, it's all in your different query WHERE clauses...
Your complex query returning the wrong results has
(join conditions between other tables)
AND swuserorganizations.organizationname = '%s'
AND ( swtickets.ticketstatustitle = 'Closed'
OR swtickets.ticketstatustitle = 'Completed' )
AND FROM_UNIXTIME(`workDateline`) >= '%s'
AND FROM_UNIXTIME(`workDateline`) <= '%s'
Your Other query has
FROM swtickettimetracks
WHERE FROM_UNIXTIME(swtickettimetracks.workdateline) >= '2013-04-16'
AND FROM_UNIXTIME(swtickettimetracks.workdateline) <= '2013-04-18'
So I would consider a few things. The first where has
FROM_UNIXTIME >= '%s' and FROM_UNIXTIME <= '%s'
Are you sure the '%s' values are properly formatted to match the '2013-04-16' and '2013-04-18' format sample?
But more importantly, your first query is using the same date range (if correct), but is also only getting those for specific organization name AND (Closed or Completed) records. So, if the second query is returning 100 records, but the main query only 70, then are the other 30 some status other than closed/completed, or a different organization? In addition, if the join tables don't have matching IDs that would prevent those with invalid IDs from being returned. The only way to confirm that is to change to LEFT-JOIN syntax on those tables and see the results.

Related

mysql GROUP CONCAT not returning values

Here is my query
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
AND DATE(o.payment_on) = DATE(NOW())
above query return null when i run this query in terminal
When i use subquery below it returns data
SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92)
above query returns
'106,124,142,179'
and when i run my first query like below
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (106,124,142,179))
AND DATE(o.payment_on) = DATE(NOW())
it return me value.
Why it is not working with subquery please help
This does not do what you want:
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
This compares a single value against a comma-separated list of values, so it never matches (unless there is just one row in users for the given team).
You could phrase this as:
AND assign_sale_id IN (SELECT id FROM users WHERE team_id = 92)
But this would probably be more efficently expressed with exists:
AND EXISTS(SELECT 1 FROM users u WHERE u.team_id = 92 AND u.id = o.assign_sale_id)
Side note: I would also recommend rewriting this condition:
AND DATE(o.payment_on) = DATE(NOW())
To the following, which can take advantage of an index:
AND o.payment_on >= current_date AND o.payment_on < current_date + interval 1 day

How to speed up the SQL query execution time in MySQL database?

Below is my SQL query.
SELECT `left_table`.`right_table_id`, MAX(left_table.add_time) AS max_add_time
FROM `left_table`
LEFT JOIN `right_table` ON `left_table`.`right_table_id` = `right_table`.`id`
WHERE left_table.add_time <= NOW()
AND (
(right_table.some_id = 1 AND right_table.category != -2)
OR
(right_table.another_id = 1 AND right_table.category != -1)
) AND NOT(right_table.category = -3)
AND NOT(right_table.category = -4)
GROUP BY `right_table_id`
ORDER BY `max_add_time` DESC, `left_table`.`id` DESC
LIMIT 12
It takes 5356.6ms to execute this query. It takes too long to me. I have been trying and trying to speed up the execution time. But no result. How can I improve the execution time for the above query?
Hmmm . . . I would start by writing the logic like this:
SELECT COUNT(DISTINCT lt.`right_table_id`)
FROM `left_table` lt LEFT JOIN
`right_table` rt
ON lt.`right_table_id` = rt.`id`
WHERE lt.add_time <= NOW() AND
((rt.some_id = 1 AND rt.category <> -2) OR
(rt.another_id = 1 AND rt.category <> -1)
) AND
rt.category NOT IN (-3, -4);
There might be additional simplifications, depending on whether lt.right_table_id always matches a row in the right table (or is NULL). And various other considerations.

SQL query with a major NOT IN not working

Does anyone know what's wrong with this query?
This works perfectly on its own:
SELECT * FROM
(SELECT * FROM data WHERE site = '".$id."'
AND disabled = '0'
AND carvotes NOT LIKE '0'
AND (time > ( now( ) - INTERVAL 14 DAY ))
GROUP BY car ORDER BY carvotes DESC LIMIT 0 , 10)
X order by time DESC
So does this:
SELECT * FROM data WHERE site = '".$id."' AND disabled = '0' GROUP BY car DESC ORDER BY time desc LIMIT 0 , 30
But combining them like this:
SELECT * FROM data WHERE site = '".$id."' AND disabled = '0' AND car NOT IN (SELECT * FROM
(SELECT * FROM data WHERE site = '".$id."'
AND disabled = '0'
AND carvotes NOT LIKE '0'
AND (time > ( now( ) - INTERVAL 14 DAY ))
GROUP BY car ORDER BY carvotes DESC LIMIT 0 , 10)
X order by time DESC) GROUP BY car DESC ORDER BY time desc LIMIT 0 , 30
Gives errors. Any ideas?
Please try the following...
$result = mysqli_query( $con,
"SELECT *
FROM data
WHERE site = '" . $id .
"' AND disabled = '0'
AND car NOT IN ( SELECT car
FROM ( SELECT car,
carvotes
FROM data
WHERE site = '" . $id .
"' AND disabled = '0'
AND carvotes NOT LIKE '0'
AND ( time > ( NOW( ) - INTERVAL 14 DAY ) )
GROUP BY car
ORDER BY carvotes DESC
LIMIT 10 ) X
)
GROUP BY car
ORDER BY time DESC
LIMIT 30" );
The main cause of your problem is that with car NOT IN ( SELECT * FROM ( SELECT *... you are trying to compare each record's value of car with each row returned by your subquery. IN requires you to have the same number of fields on both sides of the comparison. By using SELECT * at both levels of the subquery you were ensuring that the right side of the comparison had however many fields are in data versus your single field on the left, which confused MySQL.
Since you are aiming to compare to a single field, namely car, our subquery has to select just the car field from its dataset. Since the sort order of the subquery's results has no effect upon the IN comparison, and since our innermost query will be returning just car, I have removed the outer level of the subquery.
Beyond changing the first part of the subquery to SELECT car, the only other change that I have made to the subquery is to change LIMIT 0, 10 to LIMIT 10. The former means limit to the the 10 records that are offset by 0 from the first record. This is useful if you want records 6 to 15, but redundant for 1 to 10 as LIMIT 10 has the same affect and is slightly simpler. Ditto for LIMIT 0, 30 at the end of your overall statement.
As for the main body of the statement, I have not made any attempt to specify what fields (or aggregate functions of those fields) should be returned since you have made no statement indicating what your requirements / preferences are. If you are satisfied that GROUP BY has left you with a still valid set of values, then all the good, but if not then I recommend that you rewrite your Question to be specific about that detail.
By default, MySQL sorts the data subjected to a GROUP BY into ascending order, but if an ORDER BY clause is also present then it overrides the GROUP BY's sort pattern. As such, there is no benefit to specifying DESC after either of your GROUP BY car clauses, so I have removed it where it occurs.
Interesting Sidenote : You can override a GROUP BY's sort by specifying ORDER BY NULL.
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html - on optimising your ORDER BY sorting
https://dev.mysql.com/doc/refman/5.7/en/select.html - on the SELECT statement's syntax - specifically the parts to do with LIMIT.
https://www.w3schools.com/php/php_mysql_select_limit.asp - a simpler explanation of LIMIT
This is your query:
SELECT *
FROM data
WHERE site = '".$id."' AND disabled = '0' AND
car NOT IN (SELECT *
FROM (SELECT *
FROM data
WHERE site = '".$id."' AND
disabled = '0' AND
carvotes NOT LIKE '0' AND
(time > ( now( ) - INTERVAL 14 DAY ))
GROUP BY car
ORDER BY carvotes DESC
LIMIT 0 , 10
) x
ORDER BY time DESC
)
GROUP BY car DESC
ORDER BY time desc
LIMIT 0 , 30 ;
Several comments:
Do not wrap integer constants in single quotes. This can mislead people. This can mislead optimizers.
Do not use string functions on integers (such as like). Same reason.
NOT IN with subqueries is dangerous. The construct does not handle NULL values the way you expect. Use NOT EXISTS or LEFT JOIN instead.
When using subqueries, ORDER BY is almost never appropriate.
Never use SELECT * with GROUP BY. It is just wrong. Happily, MySQL 5.7 has changed its defaults to reject this anti-pattern
So, a better way to write this query is something like this:
SELECT d.car, MAX(time) as time
FROM data d LEFT JOIN
(SELECT d2.*
FROM data d2
WHERE d2.site = '".$id."' AND
d2.disabled = 0 AND
d2.carvotes NOT LIKE 0 AND
(d2.time > ( now( ) - INTERVAL 14 DAY ))
GROUP BY d2.car
ORDER BY carvotes DESC
LIMIT 0 , 10
) car10
ON d.car = car10.car
WHERE d.site = '".$id."' AND d.disabled = 0' AND
car10.car IS NOT NULL
GROUP BY car DESC
ORDER BY MAX(time) desc
LIMIT 0 , 30 ;
Alternatively, use SELECT * and remove the GROUP BY in the outer query.

MySQL rows with maximum and specific values

I have to write a query to retrieve data from a table, using the selected date and selected category. I have written a query and it does not give correct data as expected. It should query only rows with maximum actiondate, if the action column has the value 'AD'.
INSERT INTO goldstockvaluation SELECT sh.stockid, sh.description, sh.branch, sh.grossweight, sh.pureweight, sh.purity, sh.goldcarat, sh.mcpergram, sh.goldpergram, sh.fixgold, CURDATE( )
FROM stock_history sh
JOIN (
SELECT stockid, branch,ACTION , MAX( actiondate ) AS MaxDateTime
FROM stock_history
GROUP BY stockid,branch,ACTION
)groupedsh ON sh.stockid = groupedsh.stockid
AND sh.actiondate = groupedsh.MaxDateTime
AND sh.branch = groupedsh.branch
AND sh.action = groupedsh.action
AND sh.branch = '8'
AND sh.categoryid = 'G'
AND sh.action = 'AD'
AND sh.actiondate <= '2016-03-28 23:59:59'
This is to query out the rows that have action as 'AD' and have the max(actiondate).
To query with max(action_date), you need to use sub query, e.g.:
select field1, field2
from table
where
sh.action = 'AD'
and sh.actiondate = (
select max(sh.actiondate)
from table
where sh.action = 'AD'
);

postgresql to mssql query conversion

can any one help me to convert the following query into mssql which is working on postgresql now
query is to take the updated datetime of the report in the asc order of the date
select
count(*) as count,
TO_CHAR(RH.updated_datetime,'dd-mm-YYYY') as date,
SUM(
extract (
epoch from (
RH.updated_datetime - PRI.procedure_performed_datetime
)
)
)/count(*) as average_reporting_tat
from
report R,
report_history RH,
study S,
procedure_runtime_information PRI,
priorities PP,
patient P,
procedure PR
where
RH.report_fk=R.pk and RH.pk IN (
select pk from (
select * from report_history where report_fk=r.pk order by revision desc limit 1
) as result
where old_status_fk IN (21, 27)
) AND R.study_fk = S.pk
AND S.procedure_runtime_fk = PRI.pk
AND PRI.procedure_fk = PR.pk
AND S.priority_fk = PP.pk
AND PRI.patient_fk = P.pk
AND RH.updated_datetime >= '2013-05-01'
AND RH.updated_datetime <= '2013-05-12'
group by date
If I read your query properly, your problem is that you need to list everything in the group by clause that is in your column list which is not part of an aggregate. So your group by needs to be:
GROUP BY RH.updated_datetime
If this doesn't fix it, please post the error message you are getting.