Get parent column in 3rd-level subquery - mysql

How can I get a column from the top-most parent query in a subquery within a subquery? Do I have to pass it like a variable? Here's my code:
SELECT c.in_customer_id,
(
SELECT
group_concat(the_schedule separator '\r\n')
FROM
(
SELECT
concat_ws('\n', 'Route: ', s.route_id, 'Interval: ', s.service_interval, 'Week No.: ', s.week_no, 'Weekdays: ', s.weekdays, 'Start Date: ', s.start_date, 'End Date: ', s.end_date, 'Start Time: ', s.start_time, 'End Time: ', s.end_time, '\n') AS the_schedule
FROM
schedule s
WHERE
s.service_address_id IN
(
SELECT in_customer_address_id
FROM tbl_customer_address a2
WHERE a2.in_customer_id = c.in_customer_id
)
AND s.is_skipped = '0'
GROUP BY
s.service_address_id
) a
)
AS "Schedule"
FROM
tbl_customers c
The response I get is "Error Code: 1054. Unknown column 'c.in_customer_id' in 'where clause'"

As a principle, you want to move the sub-queries in to your FROM clause.
Try something like this...
SELECT
c.in_customer_id,
s.grouped_schedule
FROM
tbl_customers AS c
LEFT JOIN
(
SELECT
in_customer_id,
group_concat(the_schedule separator '\r\n') AS grouped_schedule
FROM
(
SELECT
a.in_customer_id,
a.in_customer_address_id,
concat_ws('\n', 'Route: ', s.route_id, 'Interval: ', s.service_interval, 'Week No.: ', s.week_no, 'Weekdays: ', s.weekdays, 'Start Date: ', s.start_date, 'End Date: ', s.end_date, 'Start Time: ', s.start_time, 'End Time: ', s.end_time, '\n') AS the_schedule
FROM
tbl_customer_address AS a
INNER JOIN
schedule AS s
ON s.service_address_id = a.in_customer_address_id
WHERE
s.is_skipped = 0
)
AS schedules
GROUP BY
in_cusomer_id
)
AS s
ON s.in_customer_id = c.in_customer_id

Related

Pivoting and getting #1111 - Invalid use of group function for this query

I'm try to Pivoting and getting #1111 - Invalid use of group function for this query. I really need help here.
SELECT pack.ActionName, up.Name
SUM(IF(SUBSTRING_INDEX(SUBSTRING_INDEX(SchoolYear, ' ', 1), ' ', -1) = 'X', COUNT(tph.UniqueActionPackageId)*pack.TotalPrice, 0)) AS Total_Revenue_X,
AVERAGE(IF(SUBSTRING_INDEX(SUBSTRING_INDEX(SchoolYear, ' ', 1), ' ', -1) = 'X', Total_Revenue_X/COUNT(tph.UniqueActionPackageId),0)) AS Rataan_Kelas_X,
SUM(IF(SUBSTRING_INDEX(SUBSTRING_INDEX(SchoolYear, ' ', 1), ' ', -1) = 'XI', COUNT(tph.UniqueActionPackageId)*pack.TotalPrice, 0)) AS Total_Revenue_XI,
AVERAGE(IF(SUBSTRING_INDEX(SUBSTRING_INDEX(SchoolYear, ' ', 1), ' ', -1) = 'XI', Total_Revenue_XI/COUNT(tph.UniqueActionPackageId),0)) AS Rataan_Kelas_XI,
SUM(IF(SUBSTRING_INDEX(SUBSTRING_INDEX(SchoolYear, ' ', 1), ' ', -1) = 'XII', COUNT(tph.UniqueActionPackageId)*pack.TotalPrice, 0)) AS Total_Revenue_XII,
AVERAGE(IF(SUBSTRING_INDEX(SUBSTRING_INDEX(SchoolYear, ' ', 1), ' ', -1) = 'XII', Total_Revenue_XII/COUNT(tph.UniqueActionPackageId),0)) AS Rataan_Kelas_XII
FROM packages AS pack
JOIN transaction_package_history AS tph ON pack.ActionPackagesId = tph.UniqueActionPackageId
JOIN user_profile AS up ON up.UniqueId = tph.UserId
GROUP BY pack.ActionName;
Can you guys help me
I suspect you are trying to write something like this:
SELECT p.ActionName, up.Name
SUM(CASE WHEN SchoolYear LIKE '%X %' THEN p.TotalPrice END) as Total_Revenue_X,
AVG(CASE WHEN SchoolYear LIKE '%X %' THEN p.TotalPrice END) as Rataan_Kelas_X,
SUM(CASE WHEN SchoolYear LIKE '%XI %' THEN p.TotalPrice END) as Total_Revenue_XI,
AVG(CASE WHEN SchoolYear LIKE '%XI %' THEN p.TotalPrice END) as Rataan_Kelas_XI,
SUM(CASE WHEN SchoolYear LIKE '%XII %' THEN p.TotalPrice END) as Total_Revenue_XI,
AVG(CASE WHEN SchoolYear LIKE '%XII %' THEN p.TotalPrice END) as Rataan_Kelas_XI,
FROM packages p JOIN
transaction_package_history tph
ON p.ActionPackagesId = tph.UniqueActionPackageId JOIN
user_profile up
ON up.UniqueId = tph.UserId
GROUP BY p.ActionName;

Best way to ignore null values in MYSQL

My data looks like this,
I have multiple values in the Table field with each having its own unique values for the Data field. However, Data values for TableA will be the same format in the table myTable. Same applies to all records in the Table field.
I am using JSON_EXTRACT to get the value in conjunction with CASE statements.
SELECT
CASE
WHEN
Table = 'table A'
THEN
CONCAT_WS('\n','Name: ',
JSON_EXTRACT(Data, '$.name'))
END AS 'tableA',
CASE
WHEN
Table = 'table B'
THEN
CONCAT_WS('\n','Location: ',
JSON_EXTRACT(Data, '$.fieldType'))
END AS 'tableB' from myTable
The problem with this is that I will be getting null values.
I am getting the below results,
I am expecting the below results,
I want to avoid the null values. Is there any other way to extract the data ? I am using MYSQL.
Option one:
Select *
from ( SELECT CASE WHEN Table_c = 'table A' THEN
CONCAT_WS('\n','Name: ', JSON_EXTRACT(data_c, '$.name'))
END AS 'TableA'
from test_t) A,
( SELECT CASE WHEN Table_c = 'table B' THEN
CONCAT_WS('\n','Location: ', JSON_EXTRACT(data_c, '$.fieldType'))
END AS 'TableB'
from test_t) B
where TableA is not null
and TableB is not null
Option two:
SELECT CASE WHEN Table_c = 'table B' THEN
CONCAT_WS('\n','Location: ', JSON_EXTRACT(data_c, '$.fieldType'))
ELSE
CONCAT_WS('\n','Name: ', JSON_EXTRACT(data_c, '$.name'))
END AS 'Data'
, CASE WHEN Table_c = 'table B' THEN
'Table B'
ELSE
'Table A'
END AS 'Table'
from test_t
Here is the demo
Please note that I have changed the names of the table and columns in my demo.
I think you just want conditional aggregation:
SELECT MAX(CASE WHEN Table = 'table A'
THEN CONCAT_WS('\n', 'Name: ', JSON_EXTRACT(Data, '$.name'))
END) AS tableA,
MAX(CASE WHEN Table = 'table B'
THEN CONCAT_WS('\n','Location: ', JSON_EXTRACT(Data, '$.fieldType'))
END) AS tableB
from myTable;
If there could be multiple matches in the JSON, then you might want GROUP_CONCAT() instead:
SELECT GROUP_CONCAT(CASE WHEN Table = 'table A'
THEN CONCAT_WS('\n', 'Name: ', JSON_EXTRACT(Data, '$.name'))
END) AS tableA,
GROUP_CONCAT(CASE WHEN Table = 'table B'
THEN CONCAT_WS('\n','Location: ', JSON_EXTRACT(Data, '$.fieldType'))
END) AS tableB
from myTable;
Note: you have to understand these are not actual null values in database but because your requirement is to read and convert the rows to columns and thus each row in the final result will have null value for all columns other than the corresponding derived one. We can use max on each column (each "CASE" as in your query).
You could try with below,
SELECT MAX(IF(data_type = 'TableA', CONCAT_WS('\n', 'Name: ',
JSON_EXTRACT(data, '$.name'))
, ''))
TableA,
MAX(IF(data_type = 'TableB', CONCAT_WS('\n', 'Location: ',
JSON_EXTRACT(data, '$.fieldType')), ''))
TableB
FROM mytable

Add additional column to query based on dates in another table

I'm looking to query the datatbl table and return the table with an additional column based on the Start_date values of the productstbl table.
Below I have created a schema with the initial table (datatbl_before) and the desired end result (datatbl_after), where a Product_Lookup column has been added to the datatbl table and shows the corresponding Product from the productstbl based on the Asset_ID of the line and the Start_date.
For example, in the first row of datatbl, the row relates to Asset_ID '1' and occurred on 2017-08-16, this would retrieve the product value 'A' from the productstbl table as it is after the start_date value 2017-08-12, but before the next start_date value of 2017-09-27 related to that Asset_ID .
In the case where there is no proceeding date for the Asset_ID lookup and start_date, a 'NONE' value could be returned - e.g. Row 5 of datatbl
Can anyone provide any direction or advice on how to tackle this one?
Many thanks in advance!
CREATE TABLE datatbl_before (
PDate DATE,
Asset_ID varchar(255),
Rev int
);
CREATE TABLE datatbl_after (
PDate DATE,
Asset_ID varchar(255),
Rev int,
Product_Lookup varchar(255)
);
CREATE TABLE productstbl (
Asset_ID varchar(255),
Start_date DATE,
Product varchar(255)
);
INSERT INTO productstbl
VALUES
(' 1 ',' 2017-08-12' ,' A' ),
(' 1' ,' 2017-09-27' ,' B' ),
(' 1' ,' 2018-02-14' ,' C' ),
(' 2' ,' 2018-01-17' ,' A' ),
(' 2' ,' 2018-02-21' ,' B' ),
(' 2' ,' 2018-03-05' ,' C' );
INSERT INTO datatbl_before
VALUES
(' 2017-08-16' ,' 1' ,' 10'),
(' 2017-09-29' ,' 1' ,' 15'),
(' 2018-02-13' ,' 1' ,' 12'),
(' 2018-02-19' ,' 1' ,' 10'),
(' 2018-01-01' ,' 2' ,' 12'),
(' 2018-01-25' ,' 2' ,' 33'),
(' 2018-02-25' ,' 2' ,' 67'),
(' 2018-03-07' ,' 2' ,' 71');
INSERT INTO datatbl_after
VALUES
(' 2017-08-16' ,' 1' ,' 10' ,' A' ),
(' 2017-09-29' ,' 1' ,' 15' ,' B' ),
(' 2018-02-13' ,' 1' ,' 12' ,' B' ),
(' 2018-02-19' ,' 1' ,' 10' ,' C' ),
(' 2018-01-01' ,' 2' ,' 12' ,' NONE'),
(' 2018-01-25' ,' 2' ,' 33' ,' A' ),
(' 2018-02-25' ,' 2' ,' 67' ,' B' ),
(' 2018-03-07' ,' 2' ,' 71' ,' C' );
You can use a sub query, which goes and selects the data you want, using data from the outer query in the where clause. In this case, it finds all the products that are the right asset, and started before or on the transaction date, sorts them so the most recent one is first (the one we want) and takes only that one (The TOP 1 part).
The ISNULL part changes any missing values from NULL to 'NONE'
Select DB.*,
ISNULL(
(Select top 1 product
from productstbl
where Asset_ID = DB.Asset_ID
and Start_date <= DB.PDate
order by Start_date desc
)
, 'NONE') as Product_Lookup
from datatbl_before DB;

PIVOT data in mysql

RCDID, EmployeeID, LogDate, LogTime, TerminalID, InOut, read
3079184, 'A00075', '2009/10/28', '07:17:10 ', 'VC01 ', 'IN ', '1'
3079185, 'A00075', '2009/10/28', '17:28:51 ', 'VC01 ', 'OUT ', '1'
3079186, 'A00038', '2009/10/28', '07:29:17 ', 'VC01 ', 'IN ', '1'
3079187, 'A00038', '2009/10/28', '17:30:05 ', 'VC01 ', 'OUT ', '1'
3079188, 'A00085', '2009/10/28', '07:37:34 ', 'VC01 ', 'IN ', '1'
3079189, 'A00085', '2009/10/28', '17:43:14 ', 'VC01 ', 'IN ', '1'
Hi, above is my source table (mysql) i want to cross tab the data as follows.
EmployeeID, LogDate, In_location, in_time, Out_location, Out_time
'A00001', '2009/10/28', 'VC01', '08:37:55 ', '', '',
'A00001', '2009/10/29', 'VC01', '08:09:57 ', 'VC01 ', '17:09:32 '
'A00001', '2009/10/30', 'VC01 ', '09:48:41 ', 'VC01 ', '20:40:37 '
'A00001', '2009/11/03', 'VC01', '08:20:34 ', 'VC01 ', '18:03:34 '
'A00001', '2009/11/04', 'VC01 ', '08:26:49 ', 'VC01 ', '19:21:46 '
'A00001', '2009/11/05', 'VC01', '08:16:00 ', 'VC01 ', '19:26:01 '
can somebody help me please. i am really appreciate your help
Essentially, one needs to perform a self-join to pair up movements IN with the following OUT by the same employee on the same date:
SELECT a.EmployeeID, a.LogDate,
a.LogTime AS In_time,
MIN(b.LogTime) AS Out_time
FROM my_table a LEFT JOIN my_table b ON
a.EmployeeID = b.EmployeeID
AND a.LogDate = b.LogDate
AND a.LogTime < b.LogTime
AND b.InOut = 'OUT'
WHERE a.InOut = 'IN'
GROUP BY EmployeeID, LogDate, In_time
However, this excludes situations where there is an IN record with no corresponding OUT record. Since MySQL has no native support for FULL OUTER JOIN, one must instead use UNION to combine the above with a similar query that pairs movements OUT with the preceding IN by the same employee on the same date:
SELECT a.EmployeeID, a.LogDate,
a.LogTime AS In_time,
MIN(b.LogTime) AS Out_time
FROM my_table a LEFT JOIN my_table b ON
a.EmployeeID = b.EmployeeID
AND a.LogDate = b.LogDate
AND a.LogTime < b.LogTime
AND b.InOut = 'OUT'
WHERE a.InOut = 'IN'
GROUP BY EmployeeID, LogDate, In_time
UNION
SELECT a.EmployeeID, a.LogDate,
MAX(a.LogTime) AS In_time,
b.LogTime AS Out_time
FROM my_table a RIGHT JOIN my_table b ON
a.EmployeeID = b.EmployeeID
AND a.LogDate = b.LogDate
AND a.LogTime < b.LogTime
AND a.InOut = 'IN'
WHERE b.InOut = 'OUT'
GROUP BY EmployeeID, LogDate, Out_time
Having obtained this detail, one then needs to join the result with the table again in order to extract the terminals on which the movements were logged:
SELECT t.EmployeeID,
t.LogDate,
a.TerminalID AS In_location,
t.In_time,
b.TerminalID AS Out_location,
t.Out_time
FROM (
SELECT a.EmployeeID, a.LogDate,
a.LogTime AS In_time,
MIN(b.LogTime) AS Out_time
FROM my_table a LEFT JOIN my_table b ON
a.EmployeeID = b.EmployeeID
AND a.LogDate = b.LogDate
AND a.LogTime < b.LogTime
AND b.InOut = 'OUT'
WHERE a.InOut = 'IN'
GROUP BY EmployeeID, LogDate, In_time
UNION
SELECT a.EmployeeID, a.LogDate,
MAX(a.LogTime) AS In_time,
b.LogTime AS Out_time
FROM my_table a RIGHT JOIN my_table b ON
a.EmployeeID = b.EmployeeID
AND a.LogDate = b.LogDate
AND a.LogTime < b.LogTime
AND a.InOut = 'IN'
WHERE b.InOut = 'OUT'
GROUP BY EmployeeID, LogDate, Out_time
) t
LEFT JOIN my_table a ON
a.EmployeeID = t.EmployeeID
AND a.LogDate = t.LogDate
AND a.LogTime = t.In_time
AND a.InOut = 'IN'
LEFT JOIN my_table b ON
b.EmployeeID = t.EmployeeID
AND b.LogDate = t.LogDate
AND b.LogTime = t.Out_time
AND b.InOut = 'OUT'
See it on sqlfiddle.

SQL / Coalesce - Wrong row name

I have a problem with the request below!
REQUEST:
SELECT COALESCE(date(date_field), 'Total') AS "date_field_group",
COUNT( id_field ) AS "Nombre de bookings",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à l'achat",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à la vente",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) - SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS 'Marge',
CONCAT( REPLACE( FORMAT( (SUM( price2 ) / SUM( price1 ) ) , 2 ) , '1.', '' ) , ' ', '%') AS "Markup moyen"
FROM table1 S, table2 B
WHERE status_field
IN ( "1", "5")
AND DATE( date_field ) BETWEEN "2011-08-01" AND "2011-08-31"
AND type_field = "H"
AND price1 IS NOT NULL
AND S.id_field = B.id_field
AND B.id2 = "1"
GROUP BY date_field_group WITH ROLLUP
The thing is that the request is working fine (right numbers), but in the last line I was expected to get in first row "Total" and instead of that I got a field NULL...
Is someone know what is wrong with my request ?
Thx for any help ;).
You're query is almost correct (except for using implicit SQL '89 joins, which is an SQL anti-pattern)
The problem is in the last line: GROUP BY ... WITH ROLLUP.
The rollup gets applied very late in the process, after your COALESCE(date(date_field), 'Total').
So the coalesce has already finished by the time rollup comes along you need to rewrite the query like so:
SELECT COALESCE(date_field_group, 'Total') as date_field_group
, `Nombre de bookings`
, `Total à l'achat`
, `Total à la vente`
, `Marge`
, `Markup moyen`
FROM (
SELECT date(date_field) AS "date_field_group",
COUNT( id_field ) AS "Nombre de bookings",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à l'achat",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS "Total à la vente",
CONCAT( REPLACE( REPLACE( FORMAT( SUM( price2 ) - SUM( price1 ) , 2 ) , ',', ' ' ) , '.', ',' ) , ' €' ) AS 'Marge',
CONCAT( REPLACE( FORMAT( (SUM( price2 ) / SUM( price1 ) ) , 2 ) , '1.', '' ) , ' ', '%') AS "Markup moyen"
FROM table1 S
INNER JOIN table2 B ON (S.id_field = B.id_field)
WHERE status_field IN ( "1", "5")
AND DATE( date_field ) BETWEEN "2011-08-01" AND "2011-08-31"
AND type_field = "H"
AND price1 IS NOT NULL
AND B.id2 = "1"
GROUP BY date_field_group WITH ROLLUP ) AS subquery