GROUP_CONCAT not working in Sub-Query - mysql

I have tried this query to get the following data
SELECT
GROUP_CONCAT( sb.p_id ) ,
TRUNCATE( SUM( sb.total_amount ) , 2 ) grand_total,
TRUNCATE( SUM( sb.amount_wot ) , 2 ) sale_amount,
(SELECT TRUNCATE( SUM( (item_qty * item_price) * tax_price /100 ) , 2 )
FROM ci_bill_items
WHERE bill_id IN ( GROUP_CONCAT( sb.p_id ) ) ) tax_amount
FROM ci_suppliers_bills sb
WHERE sb.p_id >0
I got the expected result but not the tax_amount its return null but if i run the seperate query like :
SELECT TRUNCATE( SUM( (item_qty * item_price) * tax_price /100 ) , 2 )
FROM ci_bill_items
WHERE bill_id IN ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 71 )
Then i get the correct result. But all i want in one query like the first one, is group_concat not working like i tried bill_id IN ( GROUP_CONCAT( sb.p_id ) )? Help is much appriciated.

You can use FIND_IN_SET like this:
SELECT
GROUP_CONCAT( sb.p_id ) ,
TRUNCATE( SUM( sb.total_amount ) , 2 ) grand_total,
TRUNCATE( SUM( sb.amount_wot ) , 2 ) sale_amount,
(SELECT TRUNCATE( SUM( (item_qty * item_price) * tax_price /100 ) , 2 )
FROM ci_bill_items
WHERE FIND_IN_SET(bill_id, GROUP_CONCAT( sb.p_id ) ) > 0 ) tax_amount
FROM ci_suppliers_bills sb
WHERE sb.p_id >0

GROUP_CONCAT doesn't return a list, it returns a string. To get a list that you can use with IN, you need to run a subquery:
WHERE bill_id IN (SELECT p_id FROM ci_suppliers_bills
WHERE p_id > 0)

Related

Group by rows order by min price first

I have a table 'terms':
([id], [t_id], [d_start], [d_end], [price], [departure_from])
with values:
(12,10, 2018-5-5, 2018-5-15, 95, london),
(11,10, 2018-5-5, 2018-5-15, 60, london),
(10,10, 2018-5-5, 2018-5-15, 90, london),
( 9, 10, 2018-5-5, 2018-5-15, 90, prag),
( 8, 10, 2018-5-5, 2018-5-15, 85, prag),
( 7, 10, 2018-5-5, 2018-5-15, 70, prag),
( 6, 10, 2018-6-8, 2018-6-18, 30, london),
( 5, 10, 2018-6-8, 2018-6-18, 20, london),
( 4, 10, 2018-6-8, 2018-6-18, 90, london),
( 3, 10, 2018-6-8, 2018-6-18, 90, prag),
( 2, 10, 2018-6-8, 2018-6-18, 10, prag),
( 1, 10, 2018-6-8, 2018-6-18, 70, prag);
and I want to group by d_start,d_end,departure_from with order by price and I want to order by price first.
I have query:
(SELECT * FROM
(SELECT * FROM terms
WHERE t_id=10 ORDER BY price) t
GROUP BY d_start,d_end,departure_from ORDER BY d_start)
Result I want: 4 rows with min price :
(11,10, 2018-5-5, 2018-5-15, 60, london),
( 7, 10, 2018-5-5, 2018-5-15, 70, prag),
( 5, 10, 2018-6-8, 2018-6-18, 20, london),
( 2, 10, 2018-6-8, 2018-6-18, 10, prag),
My first question there. Thank you for any help.
I found result that work for me:
(SELECT * FROM
(SELECT * FROM terms
WHERE t_id=10 GROUP BY d_start,d_end,departure_from,price ORDER BY d_start) t
GROUP BY d_start,d_end,departure_from)
What do you think? It's OK?
With the information you gave, the main problem you have is the variable names. Just add an `` for every of them.
Like:
SELECT * FROM
(SELECT * FROM terms
WHERE t_id=10 ORDER BY price)
GROUP BY `start`,`end`,`from` ORDER BY start
You need to use correlated subquery for this...
select t.*
from terms t
where t.price = (
select min(t2.price)
from terms t2
where t2.d_start = t.d_start
and t2.d_end = t.d_end
and t2.departure_from = t.departure_from
)
order by t.d_start;

Recursive Dynamic Query

I have this recursive SQL query where I obtain the hierarchy of IDs of every office (an office belongs to higher rank office and such) - inspired by #leftclickben's answer in How to do the Recursive SELECT query in MySQL?:
select #pv := o.office_id, o.display_name, (
select concat(concat(group_concat(#pv := t.parent_office_id order by t.parent_office_id asc SEPARATOR '.' ), '.'), t.office_id) pivot
from (select * from office order by (CASE WHEN parent_office_id < office_id THEN parent_office_id END) DESC,
(CASE WHEN parent_office_id > office_id THEN parent_office_id END) ASC) t
where t.office_id = #pv
) 'hierarchy'
from office o
group by o.office_id
order by o.office_id asc, o.parent_office_id desc
;
For this query to work, it needs perferct descendance or ascendance to be verified. This property is not verified in my data, and thus I had to take advantage of selective order by in the subquery.
I obtained very encouraging results (80% accuracy), and I was wondering if anyone could suggest more techniques to obtain better results?
Examples:
- for some office (ID = 97), its hierarchy is 1.2.4.14.97 (accurate value);
- for another case (ID = 101), I get: 111.101 (broken hierarchy);
In a nutshell, all results must start with 1.
A good sample:
http://sqlfiddle.com/#!9/82f13/1
Based on #Hart CO's suggestion, I solved this with three queries with two unions:
(select #pv := o.office_id, o.display_name, ifnull((
select concat(concat(group_concat(#pv := t.parent_office_id order by t.parent_office_id asc SEPARATOR '.' ), '.'), t.office_id) pivot
from (select * from office order by parent_office_id desc) t
where t.office_id = #pv
), '1.') 'hierarchy'
from office o
where office_id not in (26, 27, 28, 29, 30, 32, 33, 34, 41, 57, 58, 59, 60, 61, 62, 63, 64, 73, 74, 75, 76, 77, 79, 82, 91, 96, 101, 102, 103, 104)
group by o.office_id
order by o.parent_office_id desc)
union
(
select #pv := o.office_id, o.display_name, (
select concat(concat(group_concat(#pv := t.parent_office_id order by t.parent_office_id asc SEPARATOR '.' ), '.'), t.office_id) pivot
from (select * from office order by parent_office_id DESC) t
where t.office_id = #pv
) 'hierarchy'
from office o
where office_id in (26, 27, 28, 29, 30, 32, 33, 34, 41, 57, 58, 59, 60, 61, 62, 63, 64, 91, 96, 101, 102, 103, 104)
group by o.office_id
order by o.parent_office_id desc
)
union
(select #pv := o.office_id, o.display_name, (
select concat(concat(group_concat(#pv := t.parent_office_id order by (select parent_office_id from office where office_id = t.parent_office_id) asc SEPARATOR '.' ), '.'), t.office_id) pivot
from (select * from office oo order by (select parent_office_id from office where office_id = oo.parent_office_id) deSC) t
where t.office_id = #pv
) 'hierarchy'
from office o
where office_id in (73, 74, 75, 76, 77, 79, 82)
group by o.office_id
order by o.parent_office_id desc
);
Indeed, the first query was straight forward: parent_id is smaller.
For the second query, the parent_id was larger at the bottom level.
For the third query, the parent_id of the parent is larger, that's why I opted for a sub-query in the order by block for both group_concat and the sub-query with the alias t.
I can finally move forward with my ETL.

MySQL include zero rows when using COUNT with LEFT OUTER JOIN and GROUP BY

How can I avoid eliminating the users with zero meetings? I'm aware there are similar questions, but this code is quite a bit more complex.
SELECT user.userID, user.contactName, user.email, COUNT( * ) AS meetingsCount
FROM user
LEFT OUTER JOIN meeting ON user.userID = meeting.userID
WHERE user.userID NOT
IN ( 1, 2, 3, 4, 5, 59, 62, 63, 64, 66, 69, 71, 72, 73, 78, 107 )
AND SUBSTRING( meeting.meetingCode, 5, 2 )
BETWEEN 12
AND 22
AND SUBSTRING( meeting.meetingCode, 7, 2 )
BETWEEN 01
AND 12
AND SUBSTRING( meeting.meetingCode, 9, 2 )
BETWEEN 01
AND 31
GROUP BY user.userID, contactName, email
ORDER BY meetingsCount DESC
You need to put the logic for the meeting code table in your join. Otherwise users matching the records you are filtering out from the meeting table will be filtered out of your results. Making your JOIN essentially an INNER join. I think you also should put single quotes around the values in your BETWEEN clauses.
SELECT user.userID, user.contactName, user.email, COUNT( meeting.userID ) AS meetingsCount
FROM user
LEFT OUTER JOIN meeting ON user.userID = meeting.userID
AND SUBSTRING( meeting.meetingCode, 5, 2 ) BETWEEN '12' AND '22'
AND SUBSTRING( meeting.meetingCode, 7, 2 ) BETWEEN '01' AND '12'
AND SUBSTRING( meeting.meetingCode, 9, 2 ) BETWEEN '01' AND '31'
WHERE user.userID NOT IN ( 1, 2, 3, 4, 5, 59, 62, 63, 64, 66, 69, 71, 72, 73, 78, 107 )
GROUP BY user.userID, contactName, email
ORDER BY meetingsCount DESC

MySql query and GROUP BY, what do I need to do differently?

Right now I have an sql query that selects a range of of my sales reps, then joins a customer table, and counts if certain values are true. My query works, however, It leaves out sales reps who have not sold any customers. I would like this query to return all sales reps, even if they have not created any client records matching the criteria. What do I need to change?
Here is a link to my SQL Query: http://pastie.org/4557540 (same as below)
SELECT u.id AS
`employee_id`,
u.`first_name` AS
`employee_first_name`,
u.`last_name` AS
`employee_last_name`,
(SELECT Count(*)
FROM saleset s
WHERE s.pitchedby_id = `employee_id`
AND s.pitchstartedat BETWEEN '2012-08-20 00:00:00' AND
'2012-08-20 23:59:59') AS
`transfers_taken`,
Count(IF(c.`saletype_id` IS NOT NULL, 1, NULL)) AS
`total_closes`,
Count(IF(c.`saletype_id` = 1, 1, NULL)) AS
`regular_sale`,
Count(IF(c.`saletype_id` = 2, 1, NULL)) AS
`postdated_sale`,
Count(IF(c.`saletype_id` = 4, 1, NULL)) AS
`attempted_sale`,
Count(IF(c.`customerstatus_id` IN ( 8, 18, 23 ), 1, NULL)) AS
`cancel_status`,
Count(IF(c.`customerstatus_id` IN ( 1, 12, 13, 24 ), 1, NULL)) AS
`pending_completion_status`,
Count(IF(c.`customerstatus_id` IN ( 5, 6, 16 ), 1, NULL)) AS
`complete_status`,
Count(IF(c.`customerstatus_id` = 20, 1, NULL)) AS
`postdate_pending`,
Count(IF(c.`customerstatus_id` = 25, 1, NULL)) AS
`postdate_declined`
FROM `user` u
LEFT JOIN customer c
ON c.`salesrep_id` = u.id
WHERE u.id IN ( 39, 65, 76, 96,
195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59'
GROUP BY u.`id`
The problem is that your are filtering the customers in the WHERE clause, after the LEFT JOIN is specified, so salesman with no sales get left out, because c.activationdate is null for these records.
The solution is having the client filtering inside the join conditions:
(...)
FROM `user` u
LEFT JOIN customer c
ON (c.`salesrep_id` = u.id and
u.id IN ( 39, 65, 76, 96, 195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59')
GROUP BY u.`id`
Perhaps you could take what you have an union it with your sales reps that have no sales, and just hard code them to 0. Something like:
SELECT u.id AS
`employee_id`,
u.`first_name` AS
`employee_first_name`,
u.`last_name` AS
`employee_last_name`,
(SELECT Count(*)
FROM saleset s
WHERE s.pitchedby_id = `employee_id`
AND s.pitchstartedat BETWEEN '2012-08-20 00:00:00' AND
'2012-08-20 23:59:59') AS
`transfers_taken`,
Count(IF(c.`saletype_id` IS NOT NULL, 1, NULL)) AS
`total_closes`,
Count(IF(c.`saletype_id` = 1, 1, NULL)) AS
`regular_sale`,
Count(IF(c.`saletype_id` = 2, 1, NULL)) AS
`postdated_sale`,
Count(IF(c.`saletype_id` = 4, 1, NULL)) AS
`attempted_sale`,
Count(IF(c.`customerstatus_id` IN ( 8, 18, 23 ), 1, NULL)) AS
`cancel_status`,
Count(IF(c.`customerstatus_id` IN ( 1, 12, 13, 24 ), 1, NULL)) AS
`pending_completion_status`,
Count(IF(c.`customerstatus_id` IN ( 5, 6, 16 ), 1, NULL)) AS
`complete_status`,
Count(IF(c.`customerstatus_id` = 20, 1, NULL)) AS
`postdate_pending`,
Count(IF(c.`customerstatus_id` = 25, 1, NULL)) AS
`postdate_declined`
FROM `user` u
INNER JOIN customer c
ON c.`salesrep_id` = u.id
WHERE u.id IN ( 39, 65, 76, 96,
195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59'
GROUP BY u.`id`
UNION
SELECT u.id AS
`employee_id`,
u.`first_name` AS
`employee_first_name`,
u.`last_name` AS
`employee_last_name`,
0 AS
`transfers_taken`,
Count(IF(c.`saletype_id` IS NOT NULL, 1, NULL)) AS
`total_closes`,
Count(IF(c.`saletype_id` = 1, 1, NULL)) AS
`regular_sale`,
Count(IF(c.`saletype_id` = 2, 1, NULL)) AS
`postdated_sale`,
Count(IF(c.`saletype_id` = 4, 1, NULL)) AS
`attempted_sale`,
Count(IF(c.`customerstatus_id` IN ( 8, 18, 23 ), 1, NULL)) AS
`cancel_status`,
Count(IF(c.`customerstatus_id` IN ( 1, 12, 13, 24 ), 1, NULL)) AS
`pending_completion_status`,
Count(IF(c.`customerstatus_id` IN ( 5, 6, 16 ), 1, NULL)) AS
`complete_status`,
Count(IF(c.`customerstatus_id` = 20, 1, NULL)) AS
`postdate_pending`,
Count(IF(c.`customerstatus_id` = 25, 1, NULL)) AS
`postdate_declined`
FROM `user` u
LEFT JOIN customer c
ON c.`salesrep_id` = u.id
WHERE u.id IN ( 39, 65, 76, 96,
195, 266, 349, 401,
402, 404, 405, 407,
411, 412 )
AND c.`activationdate` BETWEEN
'2012-08-20 00:00:00' AND '2012-08-20 23:59:59'
AND c.`salesrep_id` IS NULL
GROUP BY u.`id`

find a duplicate series in SQL

I have a table with 3 columns containing a variable number of records based off of the first column which is a foreign key. I am trying to determine if I can detect when there is a duplicate across multiple rows for an entire series
declare #finddupseries table
(
portid int,
asset_id int,
allocation float
)
;
INSERT INTO #finddupseries
SELECT 250, 6, 0.05 UNION ALL
SELECT 250, 66, 0.8 UNION ALL
SELECT 250, 2, 0.105 UNION ALL
SELECT 250, 4, 0.0225 UNION ALL
SELECT 250, 5, 0.0225 UNION ALL
SELECT 251, 13, 0.6 UNION ALL
SELECT 251, 2, 0.3 UNION ALL
SELECT 251, 5, 0.1 UNION ALL
SELECT 252, 13, 0.8 UNION ALL
SELECT 252, 2, 0.15 UNION ALL
SELECT 252, 5, 0.05 UNION ALL
SELECT 253, 13, 0.4 UNION ALL
SELECT 253, 2, 0.45 UNION ALL
SELECT 253, 5, 0.15 UNION ALL
SELECT 254, 6, 0.05 UNION ALL
SELECT 254, 66, 0.8 UNION ALL
SELECT 254, 2, 0.105 UNION ALL
SELECT 254, 4, 0.0225 UNION ALL
SELECT 254, 5, 0.0225
select * from #finddupseries
The records for portid 250 and 254 match.
Is there any way I can write a query to detect this?
edit: yes, the entire series must match. Also, if there was a way to determine which one it DID match would be helpful as the actual table has around 10k records.
thanks!
This query will give you all the values converted into a string grouped by port_id
SELECT fus1.portid,
(
SELECT CONVERT (VARCHAR, fus2.asset_id) + CONVERT (VARCHAR, fus2.allocation) + ','
FROM #finddupseries fus2
WHERE 1=1
AND fus1.portid = fus2.portid
ORDER BY fus2.portid, fus2.asset_id, fus2.allocation
FOR XML PATH ('')
) AllValuesFromAllRows
FROM #finddupseries fus1
GROUP BY fus1.portid
the output should look like this
portid AllValuesFromAllRows
----------- ------------------------------------------------------
250 20.105,40.0225,50.0225,60.05,660.8,
251 20.3,50.1,130.6,
252 20.15,50.05,130.8,
253 20.45,50.15,130.4,
254 20.105,40.0225,50.0225,60.05,660.8,
Now, lets do a group by with a having!
;With DuplicateFinder as
(
SELECT fus1.portid,
(
SELECT CONVERT (VARCHAR, fus2.asset_id) + CONVERT (VARCHAR, fus2.allocation) + ','
FROM #finddupseries fus2
WHERE 1=1
AND fus1.portid = fus2.portid
ORDER BY fus2.portid, fus2.asset_id, fus2.allocation
FOR XML PATH ('')
) AllValuesFromAllRows
FROM #finddupseries fus1
GROUP BY fus1.portid
)
SELECT AllValuesFromAllRows, COUNT (*) NumDups
FROM DuplicateFinder
GROUP BY AllValuesFromAllRows
Having COUNT (*) > 1
You should get
AllValuesFromAllRows NumDups
----------------------------------------------- -----------
20.105,40.0225,50.0225,60.05,660.8, 2
So here is everything put together
SET NOCOUNT ON
declare #finddupseries table
(
portid int,
asset_id int,
allocation float
)
;
INSERT INTO #finddupseries
SELECT 250, 6, 0.05 UNION ALL
SELECT 250, 66, 0.8 UNION ALL
SELECT 250, 2, 0.105 UNION ALL
SELECT 250, 4, 0.0225 UNION ALL
SELECT 250, 5, 0.0225 UNION ALL
SELECT 251, 13, 0.6 UNION ALL
SELECT 251, 2, 0.3 UNION ALL
SELECT 251, 5, 0.1 UNION ALL
SELECT 252, 13, 0.8 UNION ALL
SELECT 252, 2, 0.15 UNION ALL
SELECT 252, 5, 0.05 UNION ALL
SELECT 253, 13, 0.4 UNION ALL
SELECT 253, 2, 0.45 UNION ALL
SELECT 253, 5, 0.15 UNION ALL
SELECT 254, 6, 0.05 UNION ALL
SELECT 254, 66, 0.8 UNION ALL
SELECT 254, 2, 0.105 UNION ALL
SELECT 254, 4, 0.0225 UNION ALL
SELECT 254, 5, 0.0225
;With PivotAssetIdAndAllocation as
(
SELECT fus1.portid,
(
SELECT CONVERT (VARCHAR, fus2.asset_id) + '_'+ CONVERT (VARCHAR, fus2.allocation) + '~~'
FROM #finddupseries fus2
WHERE 1=1
AND fus1.portid = fus2.portid
ORDER BY fus2.portid, fus2.asset_id, fus2.allocation
FOR XML PATH ('')
) AllValuesFromAllRows
FROM #finddupseries fus1
GROUP BY fus1.portid
)
,
ListOfDuplicates AS
(
SELECT AllValuesFromAllRows, COUNT (*) NumDups
FROM PivotAssetIdAndAllocation
GROUP BY AllValuesFromAllRows
Having COUNT (*) > 1
)
SELECT portid, AllValuesFromAllRows
FROM PivotAssetIdAndAllocation
WHERE AllValuesFromAllRows IN (SELECT AllValuesFromAllRows FROM ListOfDuplicates)
and the output is
portid AllValuesFromAllRows
----------- ----------------------------------------------------------------------
250 2_0.105~~4_0.0225~~5_0.0225~~6_0.05~~66_0.8~~
254 2_0.105~~4_0.0225~~5_0.0225~~6_0.05~~66_0.8~~
this should do the trick:
declare #finddupseries table
(
portid int,
asset_id int,
allocation float
)
;
INSERT INTO #finddupseries
SELECT 250, 6, 0.05 UNION ALL
SELECT 250, 66, 0.8 UNION ALL
SELECT 250, 2, 0.105 UNION ALL
SELECT 250, 4, 0.0225 UNION ALL
SELECT 250, 5, 0.0225 UNION ALL
SELECT 251, 13, 0.6 UNION ALL
SELECT 251, 2, 0.3 UNION ALL
SELECT 251, 5, 0.1 UNION ALL
SELECT 252, 13, 0.8 UNION ALL
SELECT 252, 2, 0.15 UNION ALL
SELECT 252, 5, 0.05 UNION ALL
SELECT 253, 13, 0.4 UNION ALL
SELECT 253, 2, 0.45 UNION ALL
SELECT 253, 5, 0.15 UNION ALL
SELECT 254, 6, 0.05 UNION ALL
SELECT 254, 66, 0.8 UNION ALL
SELECT 254, 2, 0.105 UNION ALL
SELECT 254, 4, 0.0225 UNION ALL
SELECT 254, 5, 0.0225 UNION ALL
SELECT 255, 13, 0.6 UNION ALL
SELECT 255, 2, 0.3 UNION ALL
SELECT 255, 5, 0.1
;with cteGetDupes
as
(
SELECT row_number() over (partition by asset_id, allocation
order by portid desc, asset_id desc, allocation desc)
AS RNDesc
, row_number() over (partition by asset_id, allocation
order by portid, asset_id, allocation)
AS RNAsc
, *
FROM #finddupseries
)
SELECT portid, asset_id, allocation
FROM cteGetDupes
WHERE RNDesc - RNAsc != 0
order by portid, asset_id, allocation