Concatenation using a mysql variable - mysql

The problem in my query particularly in this piece:
#a:= concat(#a, ',', B.call_account_id) AS paid_account_id
Here is the whole query:
SELECT operator_id, paid_account_ids, SUM( goods_count * price ) AS sales_volume, count(*) AS sales_cnt
FROM (
SELECT B.operator_id, #a:= concat(#a, ',', B.call_account_id) AS paid_account_ids, B.call_time, A.goods_count, A.price, UNIX_TIMESTAMP( A.completion_date ) AS paid_ts
FROM call_module_data B
INNER JOIN ak_accounts A ON ( A.account_id = B.call_account_id AND A.goods_count >=1 )
WHERE B.call_status IN (1,7) AND A.status_id = 5
AND operator_id IN ( $op_ids )
$and_str_accounts
GROUP BY A.account_id
HAVING call_time < (paid_ts + $time_shift)
) AS T
GROUP BY operator_id";
So the expression mentioned above should produce the concatenated string of account ids (e.g 3341,4355,4433...). But I got NULL instead of desired string.
Please help to resolve. Thanks in advance.

Use GROUP_CONCAT() function instead of String concatenation
Change
#a:= concat(#a, ',', B.call_account_id) AS paid_account_id
above string to
GROUP_CONCAT(B.call_account_id) AS paid_account_ids
Final Answer:
SELECT operator_id, GROUP_CONCAT(paid_account_ids) AS paid_account_ids,
SUM(goods_count * price) AS sales_volume, COUNT(*) AS sales_cnt
FROM (SELECT B.operator_id, GROUP_CONCAT(B.call_account_id) AS paid_account_ids,
B.call_time, A.goods_count, A.price,
UNIX_TIMESTAMP(A.completion_date) AS paid_ts
FROM call_module_data B
INNER JOIN ak_accounts A ON A.account_id = B.call_account_id AND A.goods_count >=1
WHERE B.call_status IN (1,7) AND A.status_id = 5 AND operator_id IN ($op_ids)
$and_str_accounts
GROUP BY A.account_id HAVING call_time < (paid_ts + $time_shift)
) AS T
GROUP BY operator_id;

Related

GROUPed or used in an aggregate function error

I want to Group By CUSTOM_DRIVER in the query because I am getting over 1.3 million results and I just need to view them by Custom Driver for the month by store.
The query is returning me
I want something like this
I've tried using Distinct and Group By but I keep getting the error:
Attribute A.STORE_NUM must be GROUPed or used in an aggregate function
How do I get around this? The code I've been using is:
SELECT *
FROM(
SELECT
SE.STORE_NUM,
DM.MONTH_NAME_445 AS MO_445,
--DM.WEEK_START_DT_MON AS WK_BEGIN_MONDAY,
--SE.METRIC_DATE,
substring(SE.ROLLUP_NAME, 39, 20) as CUSTOM_DRIVER,
SE.DT_IMPORTED,
SE.METRIC_VALUE
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS SE
JOIN DNA_PUBLIC.ADMIN.DAY_MAP DM ON SE.METRIC_DATE = DM.DATE_DT
JOIN (
SELECT DISTINCT
METRIC_DATE,
MAX(DATE(DT_IMPORTED)) AS MAX_DT
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS
GROUP BY 1
) MX ON SE.METRIC_DATE = MX.METRIC_DATE
WHERE METRIC_NAME = 'Coverage Effectiveness'
AND SE.ROLLUP_NAME LIKE 'O%'
AND SE.METRIC_DATE between '07/27/2020' and '11/11/2020'
AND CAST(SE.DT_IMPORTED AS DATE) = MAX_DT
--AND CUSTOM_DRIVER = 'Truck/Truck'
AND SE.INDICATOR_NAME = 'Required'
--and STORE_NUM = 1
) AS A
Order by STORE_NUM
A work around was to select the values and group by the order of the selected variables. Thanks for the all the input!
SELECT
STORE_NUM,
MO_445,
CUSTOM_DRIVER,
SUM(METRIC_VALUE) as Tot_Hrs
FROM(
SELECT
SE.STORE_NUM,
DM.MONTH_NAME_445 AS MO_445,
DM.WEEK_START_DT_MON AS WK_BEGIN_MONDAY,
SE.METRIC_DATE,
substring(SE.ROLLUP_NAME, 39, 20) as CUSTOM_DRIVER,
SE.DT_IMPORTED,
SE.METRIC_VALUE
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS SE
JOIN DNA_PUBLIC.ADMIN.DAY_MAP DM ON SE.METRIC_DATE = DM.DATE_DT
JOIN (
SELECT DISTINCT
METRIC_DATE,
MAX(DATE(DT_IMPORTED)) AS MAX_DT
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS
GROUP BY 1
) MX ON SE.METRIC_DATE = MX.METRIC_DATE
WHERE METRIC_NAME = 'Coverage Effectiveness'
AND SE.ROLLUP_NAME LIKE 'O%'
AND SE.METRIC_DATE between '07/27/2020' and '11/11/2020'
AND CAST(SE.DT_IMPORTED AS DATE) = MAX_DT
--AND CUSTOM_DRIVER = 'Truck/Truck'
AND SE.INDICATOR_NAME = 'Required'
and STORE_NUM = 1
) AS A
group by 1,2,3
Order by STORE_NUM

SQL join table lower date and lower id

I have got the following two tables
START AND REPEAT
START
INSPECID=======SCORE
1--------------3
2--------------1
3--------------4
REPEAT
ID========INSPECID========SCORE========DATE
1---------1---------------9------------12/01/2016
2---------1---------------1------------11/01/2016
3---------2---------------2------------29/01/2016
4---------2---------------4------------01/01/2016
5---------2---------------3------------22/01/2016
6---------2---------------5------------02/01/2016
7---------2---------------1------------11/01/2016
8---------2---------------1------------01/01/2016
9---------3---------------1------------02/01/2016
10--------3---------------2------------09/01/2016
I am expecting as below
INCREASED------1
DECREASED------2
EQUAL----------0
Rules
1) Join tables by INSPECID
2) When more than 1 INSPECID is found in REPEAT table consider the score from the lower date.
3) when both INSPECID is matched and date is matched than consider the lower ID in the REPEAT table, so ID 4 and ID 8 has same date and same INPECTID but consider the ID 4 score which is 4.
Do a self join with REPEAT table to pick the oldest row
select s.*,a.*
from `START` s
join `REPEAT` a on s.INSPECID = a.INSPECID
left join `REPEAT` b on a.INSPECID = b.INSPECID
and case when a.DATE = b.DATE
then a.ID > b.ID
else a.DATE > b.DATE
end
where b.INSPECID is null
For conflict when INSPECID and DATE is same use CASE to choose row with lowest ID
Demo
Updated for desired result set
select t.result,count(t1.result) cnt
from (
select 'Increased' result
union
select 'Decreased' result
union
select 'Equal' result
) t
left join (
select s.score,a.id,a.DATE,
case when s.SCORE > a.SCORE
then 'Increased'
when s.SCORE < a.SCORE
then 'Decreased'
else 'Equal'
end result
from `START` s
join `REPEAT` a on s.INSPECID = a.INSPECID
left join `REPEAT` b on a.INSPECID = b.INSPECID
and case when a.DATE = b.DATE
then a.ID > b.ID
else a.DATE > b.DATE
end
where b.INSPECID is null
) t1 using(result)
group by t.result
Demo
This is a bit tricky. The following uses the group_concat() trick for calculating the first and last scores. It then puts these into the categories that you want:
select w.which, count(r.INSPECID)
from (select 'DECREASING' as which union all
select 'INCREASING' as which union all
select 'EQUAL' as which
) w left join
(select r.INSPECID,
(substring_index(group_concat(score order by date), ',', 1) + 0) as first_score,
(substring_index(group_concat(score order by date desc), ',', 1) + 1) as last_score
from repeat r
group by INSPECID
) r
ON (last_score > first_score and w.which = 'INCREASING') or
(last_score < first_score and w.which = 'DECREASING') or
(last_score = first_score and w.which = 'INCREASING')
group by w.which;
Note that the first table is not necessary.

Mysql - Access column from grandparent's joined table in WHERE in subquery inside parent's FROM

Inside a WHERE inside a subquery inside a FROM inside another subquery inside a SELECT that's joined to another table, I need to access a column from that joined table.
edited to add more complete example:
SELECT
field_one,
field_two,
field_three,
field_one-field_three AS field_five,
field_six
FROM (
SELECT
IFNULL(
(
SELECT
SUM(us.field_seven) AS field_one
FROM
table_one us
WHERE
us.rto_id = rto.relevant_field_one
AND
us.created >= (
SELECT
IF(
selected_date IS NULL,
MIN(created),
selected_date
)
FROM (
SELECT
IF(
latest_date < DATE_SUB(CURDATE(), INTERVAL rtt.relevant_field_two DAY),
CURDATE(),
MAX(prevdate)
) AS selected_date,
created
FROM (
SELECT
created,
#calc_prevdate as prevdate,
DATEDIFF(#calc_prevdate, created) AS diff,
#calc_prevdate := created
FROM (
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) AS x
CROSS JOIN (
SELECT
#calc_prevdate := NULL
) as vars
) AS z
CROSS JOIN (
SELECT
MAX(created) AS latest_date
FROM(
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) as z
) AS y
WHERE
diff > rtt.relevant_field_two
) as w
)
GROUP BY us.rto_id
),0
) AS field_one,
IFNULL(
(
SELECT
SUM(tt.field_seven) AS field_three
FROM
table_two tt
WHERE
tt.rto_id = rto.relevant_field_one
AND
tt.created >= (
SELECT
IF(
selected_date IS NULL,
MIN(created),
selected_date
)
FROM (
SELECT
IF(
latest_date < DATE_SUB(CURDATE(), INTERVAL rtt.relevant_field_two DAY),
CURDATE(),
MAX(prevdate)
) AS selected_date,
created
FROM (
SELECT
created,
#calc_prevdate as prevdate,
DATEDIFF(#calc_prevdate, created) AS diff,
#calc_prevdate := created
FROM (
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) AS x
CROSS JOIN (
SELECT
#calc_prevdate := NULL
) as vars
) AS z
CROSS JOIN (
SELECT
MAX(created) AS latest_date
FROM(
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) as z
) AS y
WHERE
diff > rtt.relevant_field_two
) as w
)
GROUP BY tt.rto_id
), 0
) AS field_three,
IFNULL(
(
SELECT
COUNT(*) AS field_two
FROM
table_two tt
WHERE
tt.rto_id = rto.relevant_field_one
GROUP BY tt.rto_id
), 0
) AS field_two,
IFNULL(
(
SELECT
GREATEST(
IFNULL(MAX(us.created), 0), IFNULL(MAX(tt.created), 0)
) AS field_six
FROM
table_one us
LEFT JOIN
table_two tt ON us.rto_id = tt.rto_id
WHERE
us.rto_id = rto.relevant_field_one
GROUP BY us.rto_id
), 0
) AS field_six
FROM
relevant_table_one rto
LEFT JOIN
relevant_table_two rtt ON rto.rtt_id = rtt.id
WHERE
rto.rtt_id = ?
GROUP BY rto.relevant_field_one
) v
ORDER BY id ASC;
given that query, I need to access relevant_table_one.relevant_field_one and relevant_table_two.relevant_field_two from inside the subqueries, but the restrictions on subqueries dictates that you cant access a parents table in a subquery inside a FROM
I managed to solve this (so far I think) by adding #rfo := relevant_field_one and #rft := relevant_field_two up in the select where they were accessable and then referring to the created variables instead of the columns down in the nested query where relevant.
It's possible I'm just getting false positives but so far the solution appears to be working.

MYSQL GROUP BY multiple derived tables?

I have this query which does some calculations based on some derived tables that are linked with an INNER JOIN.
At the moment I have a WHERE clause which pulls out one id at a time. But how can I make it list all the ids?
I have tried GROUP BY in various places but can't figure it out.
My query so far is as follows:
SELECT
equipment_id,
service_duration,
available_duration,
(available_duration / service_duration)*100 AS availability
FROM (
SELECT
SUM(service_end_time - service_start_time) AS service_duration
FROM(
SELECT equipment_id,
(CASE
END) AS service_start_time,
(CASE
END) AS service_end_time
FROM t1
WHERE equipment_id = 'EX123'
)AS A
) AS B
JOIN(
SELECT equipment_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM (
SELECT equipment_id,
(CASE
END) AS available_start_time,
(CASE
END) AS available_end_time
FROM t2
WHERE equipment_id = 'EX123'
) AS C
) AS D
ON equipment_id=D.equipment_id
What I want to do is replace the WHERE clause with a GROUP BY to list all the ids, or similar, but getting that to work is beyond my skill level... Any help greatly appreciated :)
Try below:
SELECT
equipment_id, service_duration, available_duration,
(available_duration / service_duration)*100 AS availability
FROM
(
SELECT equipment_id,
SUM(service_end_time - service_start_time) AS service_duration
FROM
(
SELECT equipment_id,
(CASE ... END) AS service_start_time,
(CASE ... END) AS service_end_time
FROM t1
) AS A
GROUP BY equipment_id
) AS B
JOIN
(
SELECT equipment_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM
(
SELECT equipment_id,
(CASE ... END) AS available_start_time,
(CASE ... END) AS available_end_time
FROM t2
) AS C
GROUP BY equipment_id
) AS D
ON equipment_id=D.equipment_id
Try this (replace my field names with your field names):
SELECT
a.emp_id,
service_duration,
available_duration
FROM
(
SELECT
emp_id,
SUM(service_end_time - service_start_time) AS service_duration
FROM
data
GROUP BY
emp_id
) a
JOIN
(
SELECT
emp_id,
SUM(available_end_time - available_start_time) AS available_duration
FROM
data
GROUP BY
emp_id
) b
ON a.emp_id = b.emp_id
GROUP BY
a.emp_id

mysql self join

I have a table called receiving with 4 columns:
id, date, volume, volume_units
The volume units are always stored as a value of either "Lbs" or "Gals".
I am trying to write an SQL query to get the sum of the volumes in Lbs and Gals for a specific date range. Something along the lines of: (which doesn't work)
SELECT sum(p1.volume) as lbs,
p1.volume_units,
sum(p2.volume) as gals,
p2.volume_units
FROM receiving as p1, receiving as p2
where p1.volume_units = 'Lbs'
and p2.volume_units = 'Gals'
and p1.date between "2012-01-01" and "2012-03-07"
and p2.date between "2012-01-01" and "2012-03-07"
When I run these queries separately the results are way off. I know the join is wrong here, but I don't know what I am doing wrong to fix it.
SELECT SUM(volume) AS total_sum,
volume_units
FROM receiving
WHERE `date` BETWEEN '2012-01-01'
AND '2012-03-07'
GROUP BY volume_units
You can achieve this in one query by using IF(condition,then,else) within the SUM:
SELECT SUM(IF(volume_units="Lbs",volume,0)) as lbs,
SUM(IF(volume_units="Gals",volume,0)) as gals,
FROM receiving
WHERE `date` between "2012-01-01" and "2012-03-07"
This only adds volume if it is of the right unit.
This query will display the totals for each ID.
SELECT s.`id`,
CONCAT(s.TotalLbsVolume, ' ', 'lbs') as TotalLBS,
CONCAT(s.TotalGalVolume, ' ', 'gals') as TotalGAL
FROM
(
SELECT `id`, SUM(`volume`) as TotalLbsVolume
FROM Receiving a INNER JOIN
(
SELECT `id`, SUM(`volume`) as TotalGalVolume
FROM Receiving
WHERE (volume_units = 'Gals') AND
(`date` between '2012-01-01' and '2012-03-07')
GROUP BY `id`
) b ON a.`id` = b.`id`
WHERE (volume_units = 'Lbs') AND
(`date` between '2012-01-01' and '2012-03-07')
GROUP BY `id`
) s
this is a cross join with no visible condition on the join, i don't think you meant that
if you want to sum quantities you don't need to join at all, just group as zerkms did
You can simply group by date and volume_units without self-join.
SELECT date, volume_units, sum(volume) sum_vol
FROM receving
WHERE date between "2012-01-01" and "2012-03-07"
GROUP BY date, volume_units
Sample test:
select d, vol_units, sum(vol) sum_vol
from
(
select 1 id, '2012-03-07' d, 1 vol, 'lbs' vol_units
union
select 2 id, '2012-03-07' d, 2 vol, 'Gals' vol_units
union
select 3 id, '2012-03-08' d, 1 vol, 'lbs' vol_units
union
select 4 id, '2012-03-08' d, 2 vol, 'Gals' vol_units
union
select 5 id, '2012-03-07' d, 10 vol, 'lbs' vol_units
) t
group by d, vol_units