mysql order by in this request - mysql

how can i add an "order by created_on asc" in this request :
(select user.first_name as prenom, user.last_name as nom, fvll.created_on, fvll.bar_code, "R" from stk_fuel_voucher_line fvll,stk_fuel_voucher fv, adm_user user
where YEAR(fvll.created_on)=? and MONTH(fvll.created_on) = ? and user.id=fv.id_user and fv.id=fvll.id_fuel_voucher and
fvll.bar_code not in
(select fvl.bar_code
from stk_fuel_voucher_line fvl, stk_fuel_voucher_book fvb
where fvl.bar_code >= fvb.first_bar_code and fvl.bar_code <=fvb.last_bar_code
and YEAR(fvl.created_on)=? and MONTH(fvl.created_on) = ?))
UNION
(select user2.first_name as prenom, user2.last_name as nom, fvll2.created_on, fvll2.bar_code, "B"
from stk_fuel_voucher fv2, stk_fuel_voucher_book fvb2, stk_fuel_voucher_line fvll2, adm_user user2
where fvll2.bar_code >= fvb2.first_bar_code and fvll2.bar_code <=fvb2.last_bar_code and user2.id=fv2.id_user and fv2.id=fvll2.id_fuel_voucher
and YEAR(fvll2.created_on)=? and MONTH(fvll2.created_on) = ?)

It looks to me like the two queries are the same, with the only difference being detecting whether there's a matching bar_code, and returning 'B' or 'R' depending.
I'd avoid the redundant rigmarole of the UNION and just do one query, with a conditional test to determine whether a 'B' or 'R' is returned.
If the intent of the UNION operator (in place of the more usual UNION ALL) is to remove duplicates from each set, we can use a GROUP BY clause or DISTINCT keyword to achieve that. (In the original query, we are guaranteed that there won't be duplicates between the two sets, on set always as an 'R', the other set always has a 'B'.
I don't have an understanding of the specification for the query, but based on what I am able to discern from the existing query, I would tend to do something like this instead:
SELECT user.first_name AS prenom
, user.last_name AS nom
, fvll.created_on AS created_on
, fvll.bar_code
, CASE WHEN ni.bar_code IS NULL THEN 'R' ELSE 'B' END AS r
FROM stk_fuel_voucher_line fvll
JOIN stk_fuel_voucher fv
ON fv.id = fvll.id_fuel_voucher
JOIN adm_user user
ON user.id = fv.id_user
LEFT
JOIN ( SELECT fvl.bar_code
FROM stk_fuel_voucher_line fvl
JOIN stk_fuel_voucher_book fvb
ON fvb.first_bar_code <= fvl.bar_code
AND fvb.last_bar_code >= fvl.bar_code
WHERE YEAR(fvl.created_on) = ?
AND MONTH(fvl.created_on) = ?
GROUP BY fvl.bar_code
) ni
ON ni.bar_code = fvll.bar_code
WHERE YEAR(fvll.created_on) = ?
AND MONTH(fvll.created_on) = ?
GROUP
BY user.first_name AS prenom
, user.last_name AS nom
, fvll.created_on
, fvll.bar_code
, CASE WHEN ni.bar_code IS NULL THEN 'R' ELSE 'B' END
ORDER
BY fv11.created_on
Again, if we aren't concerned with removing duplicates, then we could remove the GROUP BY clause.
For the date comparisons, I'd opt for comparing the raw dates, so the query could make effective use of an index range scan operation.
Rather than this:
WHERE YEAR(fvll.created_on) = ?
AND MONTH(fvll.created_on) = ?
I would write something like
WHERE fvll.created_on >= month_begin_dt + INTERVAL 0 MONTH
AND fvll.created_on >= month_begin_dt + INTERVAL 1 MONTH
with month_begin_dt representing an expression that returns the first day of the month, however that needs to get passed in, if we need to construct a DATE from a year and a month, we could do that. The end goal would be to have equivalent to:
WHERE fvll.created_on >= '2018-05-01' + INTERVAL 0 MONTH
AND fvll.created_on >= '2018-05-01' + INTERVAL 1 MONTH

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

date_format(from_unixtime()) subselect very slow

I have the following query I'm trying to use to spit out each day in a date range and show the # of leads, assignments, & returns:
select
date_format(from_unixtime(date_created), '%m/%d/%Y') as date_format,
(select count(distinct(id_lead)) from lead_history where (date_format(from_unixtime(date_created), '%m/%d/%Y') = date_format) and (id_vertical in (2)) and (id_website in (3,8))) as leads,
(select count(id) from assignments where deleted=0 and (date_format(from_unixtime(date_assigned), '%m/%d/%Y') = date_format) and (id_vertical in (2)) and (id_website in (3,8))) as assignments,
(select count(id) from assignments where deleted=1 and (date_format(from_unixtime(date_deleted), '%m/%d/%Y') = date_format) and (id_vertical in (2)) and (id_website in (3,8))) as returns
from lead_history
where date_created between 1509494400 and 1512086399
group by date_format
The date_created, date_assigned, and date_deleted fields are integers representing timestamps. id, id_lead, id_vertical and id_website are already indexed.
Would adding indexes to date_created, date_assigned, date_deleted, and deleted help make this faster? The issue I'm having is that it is very slow, and I'm not sure an index will help when using date_format(from_unixtime(...
Here is the EXPLAIN:
Looking to your code you could rewrite the query as ..
select
date_format(from_unixtime(date_created), '%m/%d/%Y') as date_format
, count(distinct(h.id_lead) as leads
, sum(case a.deleted = 1 then 1 else 0 end) assignments
, sum(case b.deleted = 0 then 1 else 0 end) returns
from lead_history h
inner join assignments on a a.date_assigned = h.date_created
and a.id_vertical = 2
and id_website in (3,8))
inner join assignments on b b.deleted = h.date_created
and a.id_vertical = 2
and id_website in (3,8))
where date_created between 1509494400 and 1512086399
group by date_format
anyway you shold avoid unuseful () and nested (), avoid unuseful conversion between date and use join instead of subselect .. or at least reduce similar sabuselect using case
PS for what concern the index remember that the use of conversion on a column value invalid the use of related the index ..

Simplify CASE expression used multiple times

For readability, I would like to modify the below statement. Is there a way to extract the CASE statement, so I can use it multiple times without having to write it out every time?
select
mturk_worker.notes,
worker_id,
count(worker_id) answers,
count(episode_has_accepted_imdb_url) scored,
sum( case when isnull(imdb_url) and isnull(accepted_imdb_url) then 1
when imdb_url = accepted_imdb_url then 1
else 0 end ) correct,
100 * ( sum( case when isnull(imdb_url) and isnull(accepted_imdb_url) then 1
when imdb_url = accepted_imdb_url then 1
else 0 end)
/ count(episode_has_accepted_imdb_url) ) percentage
from
mturk_completion
inner join mturk_worker using (worker_id)
where
timestamp > '2015-02-01'
group by
worker_id
order by
percentage desc,
correct desc
You can actually eliminate the case statements. MySQL will interpret boolean expressions as integers in a numeric context (with 1 being true and 0 being false):
select mturk_worker.notes, worker_id, count(worker_id) answers,
count(episode_has_accepted_imdb_url) scored,
sum(imdb_url = accepted_imdb_url or imdb_url is null and accepted_idb_url is null) as correct,
(100 * sum(imdb_url = accepted_imdb_url or imdb_url is null and accepted_idb_url is null) / count(episode_has_accepted_imdb_url)
) as percentage
from mturk_completion inner join
mturk_worker
using (worker_id)
where timestamp > '2015-02-01'
group by worker_id
order by percentage desc, correct desc;
If you like, you can simplify it further by using the null-safe equals operator:
select mturk_worker.notes, worker_id, count(worker_id) answers,
count(episode_has_accepted_imdb_url) scored,
sum(imdb_url <=> accepted_imdb_url) as correct,
(100 * sum(imdb_url <=> accepted_imdb_url) / count(episode_has_accepted_imdb_url)
) as percentage
from mturk_completion inner join
mturk_worker
using (worker_id)
where timestamp > '2015-02-01'
group by worker_id
order by percentage desc, correct desc;
This isn't standard SQL, but it is perfectly fine in MySQL.
Otherwise, you would need to use a subquery, and there is additional overhead in MySQL associated with subqueries.

PHP MySQL Group By question

I have a column inside my table: tbl_customers that distinguishes a customer record as either a LEAD or a CUS.
The column is simply: recordtype, with is a char(1). I populate it with either C, or L.
Obviously C = customer, while L = lead.
I want to run a query that groups by the day the record was created, so I have a column called: datecreated.
Here's where I get confused with the grouping.
I want to display a result (in one query) the COUNT of customers and the COUNT of leads for a particular day, or date range. I'm successful with only pulling the number for either recordtype:C or recordtype:L , but that takes 2 queries.
Here's what I have so far:
SELECT COUNT(customerid) AS `count`, datecreated
FROM `tbl_customers`
WHERE `datecreated` BETWEEN '$startdate."' AND '".$enddate."'
AND `recordtype` = 'C'
GROUP BY `datecreated` ASC
As expected, this displays 2 columns (the count of customer records and the datecreated).
Is there a way to display both in one query, while still grouping by the datecreated column?
You can do a group by with over multiple columns.
SELECT COUNT(customerid) AS `count`, datecreated, `recordtype`
FROM `tbl_customers`
WHERE `datecreated` BETWEEN '$startdate."' AND '".$enddate."'
GROUP BY `datecreated` ASC, `recordtype`
SELECT COUNT(customerid) AS `count`,
datecreated,
SUM(`recordtype` = 'C') AS CountOfC,
SUM(`recordtype` = 'L') AS CountOfL
FROM `tbl_customers`
WHERE `datecreated` BETWEEN '$startdate."' AND '".$enddate."'
GROUP BY `datecreated` ASC
See Is it possible to count two columns in the same query
There are two solutions, depending on whether you want the two counts in separate rows or in separate columns.
In separate rows:
SELECT datecreated, recordtype, COUNT(*)
FROM tbl_customers
WHERE datecreated BETWEEN '...' AND '...'
GROUP BY datecreated, recordtype
In separate colums (this is called pivoting the table)
SELECT datecreated,
SUM(recordtype = 'C') AS count_customers,
SUM(recordtype = 'L') AS count_leads
FROM tbl_customers
WHERE datecreated BETWEEN '...' AND '...'
GROUP BY datecreated
Use:
$query = sprintf("SELECT COUNT(c.customerid) AS count,
c.datecreated,
SUM(CASE WHEN c.recordtype = 'C' THEN 1 ELSE 0 END) AS CountOfC,
SUM(CASE WHEN c.recordtype = 'L' THEN 1 ELSE 0 END) AS CountOfL
FROM tbl_customers c
WHERE c.datecreated BETWEEN STR_TO_DATE('%s', '%Y-%m-%d %H:%i')
AND STR_TO_DATE('%s', '%Y-%m-%d %H:%i')
GROUP BY c.datecreated",
$startdate, $enddate);
You need to fill out the date format - see STR_TO_DATE for details.

SQL Select syntax error

thanks for your help yesterday.
I am now trying to incorporate the query from yesterday into an existing query so I can show the highest itemcode's reporting group in the existing query..but I have a syntax error somewhere at my Select statement.
ERROR: Keyword SELECT not expected.
I have tried putting brackets at every possible place but still no go..can you please help? (ps-this whole query has been giving me nightmares!)
WITH CALC1 AS (SELECT OTQUOT, OTIT01 AS ITEMS, ROUND(OQCQ01 * OVRC01,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT01 <> ''
UNION ALL
SELECT OTQUOT, OTIT02 AS ITEMS, ROUND(OQCQ02 * OVRC02,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT02 <> ''
UNION ALL
SELECT OTQUOT, OTIT03 AS ITEMS, ROUND(OQCQ03 * OVRC03,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT03 <> ''
UNION ALL
SELECT OTQUOT, OTIT04 AS ITEMS, ROUND(OQCQ04 * OVRC04,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT04 <> ''
UNION ALL
SELECT OTQUOT, OTIT05 AS ITEMS, ROUND(OQCQ05 * OVRC05,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT05 <> ''
UNION ALL
SELECT OTQUOT, OTIT06 AS ITEMS, ROUND(OQCQ06 * OVRC06,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT06 <> ''
UNION ALL
SELECT OTQUOT, OTIT07 AS ITEMS, ROUND(OQCQ07 * OVRC07,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT07 <> ''
UNION ALL
SELECT OTQUOT, OTIT08 AS ITEMS, ROUND(OQCQ08 * OVRC08,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT08 <> ''
UNION ALL
SELECT OTQUOT, OTIT09 AS ITEMS, ROUND(OQCQ09 * OVRC09,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT09 <> ''
UNION ALL
SELECT OTQUOT, OTIT10 AS ITEMS, ROUND(OQCQ10 * OVRC10,2) AS COST
FROM #LIB#.RTQOTA
WHERE OTIT10 <> ''
)
(SELECT OTQUOT, DESC
FROM (
SELECT OTQUOT, ITEMS, B.IXRPGP AS GROUP, C.OTRDSC AS DESC, COST, ROW_NUMBER() OVER
(ORDER BY COST DESC) AS RN
FROM CALC1 AS A INNER JOIN #LIB#.ITMCON AS B ON (A.ITEMS = B.IKITMC) INNER JOIN
DATAGRP.GDSGRP AS C ON (B.IXRPGP = C.OKRPGP)
) T
WHERE T.RN >= 1)
SELECT
A.OKPBRN,
A.OCAREA,
A.OTCCDE,
A.OTCNAM,
A.OTSMAN,
A.OKPBRN||A.OAPNUM AS OTQUOT,
A.OTONUM,
A.OTCAD1,
A.OTCAD2,
A.OTCAD3,
A.OTPCDE,
A.OTDEL1,
A.OTDEL2,
A.OTDEL3,
CHAR(DATE(CASE WHEN SUBSTR(A.ODOQDT,5,4) = '0000' THEN '0001' ELSE SUBSTR(A.ODOQDT,5,4)
END ||'-'||
CASE WHEN SUBSTR(A.ODOQDT,4,2) = '00' THEN '01' ELSE SUBSTR(A.ODOQDT,3,2) END ||'-'||
CASE WHEN SUBSTR(A.ODOQDT,1,2) = '00' THEN '01' ELSE SUBSTR(A.ODOQDT,1,2) END), ISO) AS
ODOQDT_CCYYMMDD,
CHAR(DATE(CASE WHEN SUBSTR(A.ODDELD,7,2) = '' THEN '0001' ELSE '20'||SUBSTR(A.ODDELD,7,2)
END ||'-'||
CASE WHEN SUBSTR(A.ODDELD,4,2) = '' THEN '01' ELSE SUBSTR(A.ODDELD,4,2) END ||'-'||
CASE WHEN SUBSTR(A.ODDELD,1,2) = '' THEN '01' ELSE SUBSTR(A.ODDELD,1,2) END), ISO) AS
ODDELD_CCYYMMDD,
B.DESC,
A.OVQTVL
FROM
#LIB#.RTQCTL AS A INNER JOIN CALC1 AS B ON (A.OKPBRN||A.OAPNUM = B.OTQUOT)
WHERE
A.OKPBRN = '#OKPBRN#'
AND A.OTCCDE NOT LIKE '*DEP%'
AND CHAR(DATE(CASE WHEN SUBSTR(A.ODOQDT,5,4) = '0000' THEN '0001' ELSE SUBSTR
(A.ODOQDT,5,4) END ||'-'||
CASE WHEN SUBSTR(A.ODOQDT,4,2) = '00' THEN '01' ELSE SUBSTR(A.ODOQDT,3,2) END ||'-'||
CASE WHEN SUBSTR(A.ODOQDT,1,2) = '00' THEN '01' ELSE SUBSTR(A.ODOQDT,1,2) END), ISO) >=
CHAR(CURDATE() - 3 MONTH, ISO)
AND A.OCQF01 = '0'
AND A.OCQF02 = '0'
AND A.OCQF04 = '0'
AND A.OCQF05 = '0'
AND A.OCQF06 = '0'
AND A.OCQF07 = '0'
AND A.OCQF08 = '0'
AND A.OCQF09 = '0'
AND A.OCQF10 = '1'
AND A.OTCGRP LIKE 'S/%'
ORDER BY
A.OTSMAN ASC,
A.OVQTVL DESC,
CHAR(DATE(CASE WHEN SUBSTR(A.ODDELD,7,2) = '' THEN '0001' ELSE '20'||SUBSTR(A.ODDELD,7,2) END ||'-'||
CASE WHEN SUBSTR(A.ODDELD,4,2) = '' THEN '01' ELSE SUBSTR(A.ODDELD,4,2) END ||'-'||
CASE WHEN SUBSTR(A.ODDELD,1,2) = '' THEN '01' ELSE SUBSTR(A.ODDELD,1,2) END),ISO) ASC
When using UNION, wrap your SELECT statements in parentheses.
e.g.
(SELECT * FROM foo)
UNION ALL
(SELECT * FROM bar)
Edit: Upon closer inspection of that monstrosity of a query, there are likely numerous other errors within it. I suggest ripping parts of it out and provide us with the smallest possible example that still causes the syntax error.
It looks like you have two totally separate queries, with no semicolon between them.
This is where I think the problem is:
. . .
WHERE T.RN >= 1)
SELECT A.OKPBRN, A.OCAREA, A.OTCCDE, A.OTCNAM, A.OTSMAN,
. . .
It would really help when you ask questions to identify the brand and version of the database you're using. Different databases support different SQL language features, so the right solution depends strongly on the technology you're using. Tag your question with the brand you use.
There are many things wrong with your query:
MySQL does not support WITH common table expressions. This is what made me think you were using Oracle or Microsoft SQL Server when you asked another question related to this query yesterday.
MySQL does not support the ROW_NUMBER() windowing function. My apologies for suggesting you use this function, I had assumed you were using a database that supports WITH so I assumed ROW_NUMBER() would be supported too. You should learn to use the LIMIT clause if you use MySQL.
Trying to put a subquery that returns multiple columns and multiple rows into a single column of your select-list.
If you're trying to make the subquery part of the select-list, then SELECT belongs in front of the subquery. You should give it a column alias too.
Complex date-conversion expressions. I would recommend storing dates in a canonical ISO form.
The original design of OVRC01, OVRC02, OVRC03, etc. This is a repeating group, which violates First Normal Form. The same is true for OCQF01, OCQF02, OCQF03, etc.
I'm not sure you're using LIKE wildcards correctly. * is not a standard wildcard for LIKE.
Indecipherable table naming and column naming conventions. It looks like an Oracle database from the 1980's. Without knowing anything about your database or the problem you're trying to solve, it's impossible to recommend another solution.
It looks you have two CTE tables but only one is named. The second starts (SELECT OTQUOT, DESC(. You could name it (i.e., change it to , CALC2 As (SELECT OTQUOT, DESC(... but I haven't the foggiest idea where you are actually wanting to use this table in the rest of your query.