Union on simple MySQL recursion - mysql

Is there a way to do a UNION between the LEFT JOIN lines, so that the results are not in a separate columns (lev1, lev2, lev3 and lev4), but in a single column (i.e. "ItemNo")?
Here's the MySQL query:
SELECT t1.ItemID AS lev1, t2.ItemID as lev2, t3.ItemID as lev3, t4.ItemID as lev4
FROM TableOfRelations AS t1
LEFT JOIN TableOfRelations AS t2 ON t2.ParentItemID = t1.ItemID
LEFT JOIN TableOfRelations AS t3 ON t3.ParentItemID = t2.ItemID
LEFT JOIN TableOfRelations AS t4 ON t4.ParentItemID = t3.ItemID
WHERE t1.ParentItemID = (SELECT ID FROM TableOfItems WHERE ItemID = 3599);
EDIT:
Here is a sample of result I am looking for:
ItemID ParentItemID JoinedDescription1 JoinedDescription2
3599 NULL MyString1A MyString1B
35 3599 MyString35A MyString35B
168 3599 MyString168A MyString168B
192 168 MyString192A MyString192B
238 3599 MyString238A MyString238B
266 168 MyString266A MyString266B
This result will be used for filling up a TreeView using VB.NET. Also the "JoinedDescriptions" are yet to be joined from a different table according to ItemID, but I think I can handle that easily once I get the basic table correctly.
Important note: Those selected lines are only belonging to one item (root item "3599" in this example), meaning it's only few lines out of thousands. Some recursion examples presume that all the table lines are used in the query, which is not my case.

No, there's not really a way to do a UNION between the LEFT JOIN operations.
But you can use the resultset from your query to get the result you want, by turning your query into an inline view.
Here's one way:
SELECT u.i AS lev
, CASE u.i
WHEN 1 THEN t.lev1
WHEN 2 THEN t.lev2
WHEN 3 THEN t.lev3
WHEN 4 THEN t.lev4
END AS levItemID
FROM ( SELECT 1 AS i
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
) u
CROSS
JOIN ( SELECT t1.ItemID AS lev1
, t2.ItemID AS lev2
, t3.ItemID AS lev3
, t4.ItemID AS lev4
FROM TableOfItems t0
JOIN TableOfRelations t1
ON t1.ParentItemID = t0.id
LEFT
JOIN TableOfRelations t2
ON t2.ParentItemID = t1.ItemID
LEFT
JOIN TableOfRelations t3
ON t3.ParentItemID = t2.ItemID
LEFT
JOIN TableOfRelations t4
ON t4.ParentItemID = t3.ItemID
WHERE t0.ItemID = 3599
) t
Note: this is really more of a UNION ALL operation, it doesn't remove duplicates, it returns NULL values, and it returns the level of the parent.
You can tweak the query to get the results you want. If you don't care about the level number, remove the u.i from the SELECT list.
To remove duplicates, you can either add the DISTINCT keyword following SELECT, or add a GROUP BY clause. To eliminate NULL values, you can add a HAVING levItemID IS NOT NULL clause, etc.

I believe you are looking for concatenating strings.
See the Mysql Documentation on Concat:
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_concat

How about using a query as below.
select t1.ItemID as ItemNo
LEFT JOIN TableOfRelations AS t2 ON t2.ParentItemID = t1.ItemID
LEFT JOIN TableOfRelations AS t3 ON t3.ParentItemID = t2.ItemID
LEFT JOIN TableOfRelations AS t4 ON t4.ParentItemID = t3.ItemID
WHERE t1.ParentItemID = (SELECT ID FROM TableOfItems WHERE ItemID = 3599)
UNION
select t2.ItemID as ItemNo
LEFT JOIN TableOfRelations AS t2 ON t2.ParentItemID = t1.ItemID
LEFT JOIN TableOfRelations AS t3 ON t3.ParentItemID = t2.ItemID
LEFT JOIN TableOfRelations AS t4 ON t4.ParentItemID = t3.ItemID
WHERE t1.ParentItemID = (SELECT ID FROM TableOfItems WHERE ItemID = 3599)
UNION
select t3.ItemID as ItemNo
LEFT JOIN TableOfRelations AS t2 ON t2.ParentItemID = t1.ItemID
LEFT JOIN TableOfRelations AS t3 ON t3.ParentItemID = t2.ItemID
LEFT JOIN TableOfRelations AS t4 ON t4.ParentItemID = t3.ItemID
WHERE t1.ParentItemID = (SELECT ID FROM TableOfItems WHERE ItemID = 3599)
UNION
select t4.ItemID as ItemNo
LEFT JOIN TableOfRelations AS t2 ON t2.ParentItemID = t1.ItemID
LEFT JOIN TableOfRelations AS t3 ON t3.ParentItemID = t2.ItemID
LEFT JOIN TableOfRelations AS t4 ON t4.ParentItemID = t3.ItemID
WHERE t1.ParentItemID = (SELECT ID FROM TableOfItems WHERE ItemID = 3599)

Assuming you mean that you want the itemID on leaf nodes (I see no mention of itemNo in your query)....
SELECT
IFNULL(t4.ItemID, IFNULL(t3.ItemID, IFNULL(t2.ItemID, t1.ItemId))
(+remainder of your query)

Related

Calculating SUM() results from subqueries into a total

My brain is starting to hurt with this query and I would appreciate some guidance. What I am trying to get as the result of this query are three values: attendanceScore, lootScore, and totalScore (attendanceScore - lootScore).
Attendance is tracked across three tables: attendance, attendancelog, and attendancevalues.
attendance records an individual's attendance status attached to an attendancelog record. There are types of attendance such as "attended", "missed", and "called out".
attendancelog is the parent record which records the event type, title and date as well as who logged the attendance and when.
attendancevalues is a config table that matches the attendance type from attendance and the event type from attendancelog and returns a configurable value FLOAT.
Loot is tracked across two tables: loot and loottypes.
loot logs each individual item, who received it and when and what type of loot it was (Primary, Secondary, Free-for-all).
loottypes is a config table that takes the type from loot and returns a configurable cost FLOAT.
After some work I have come up with a working query to get attendanceScore and lootScore:
SELECT
(SELECT SUM(t3.`value`)
FROM `attendance` t1
INNER JOIN `attendancelog` t2
ON t2.`id` = t1.`attendancelog_id`
INNER JOIN `attendancevalues` t3
ON t3.`eventtype_id` = t2.`type` AND t3.`attendancetype_id` = t1.`type`
WHERE t1.`user_id` = 3) as attendanceScore,
(SELECT SUM(t2.`cost`)
FROM `loot` t1
INNER JOIN `loottypes` t2
ON t2.`id` = t1.`type`
WHERE t1.`user_id` = 3) as lootScore
I know this doesn't work, but I tried to add (attendanceScore - lootScore) to the query but it says those fields are not available. That is ultimately what I need to complete the query.
I can get the result I want by copying each of the subqueries directly into (attendanceScore - lootScore) but it is just absolutely hideous and I'm sure unnecessary:
SELECT
(SELECT SUM(t3.`value`)
FROM `attendance` t1
INNER JOIN `attendancelog` t2
ON t2.`id` = t1.`attendancelog_id`
INNER JOIN `attendancevalues` t3
ON t3.`eventtype_id` = t2.`type` AND t3.`attendancetype_id` = t1.`type`
WHERE t1.`user_id` = 3) as attendanceScore,
(SELECT SUM(t2.`cost`)
FROM `loot` t1
INNER JOIN `loottypes` t2
ON t2.`id` = t1.`type`
WHERE t1.`user_id` = 3) as lootScore,
(
(SELECT SUM(t3.`value`)
FROM `attendance` t1
INNER JOIN `attendancelog` t2
ON t2.`id` = t1.`attendancelog_id`
INNER JOIN `attendancevalues` t3
ON t3.`eventtype_id` = t2.`type` AND t3.`attendancetype_id` = t1.`type`
WHERE t1.`user_id` = 3) - (SELECT SUM(t2.`cost`)
FROM `loot` t1
INNER JOIN `loottypes` t2
ON t2.`id` = t1.`type`
WHERE t1.`user_id` = 3)
) as totalScore
Could someone help me understand what methods to use for cleaning this up into something more streamlined and efficient?
You may use an inline view
SELECT attendanceScore,
lootScore,
attendanceScore - lootScore as totalScore
FROM
(
SELECT
(
SELECT SUM(t3.`value`)
FROM `attendance` t1
INNER JOIN `attendancelog` t2
ON t2.`id` = t1.`attendancelog_id`
INNER JOIN `attendancevalues` t3
ON t3.`eventtype_id` = t2.`type` AND t3.`attendancetype_id` = t1.`type`
WHERE t1.`user_id` = 3
) as attendanceScore,
(
SELECT SUM(t2.`cost`)
FROM `loot` t1
INNER JOIN `loottypes` t2 ON t2.`id` = t1.`type`
WHERE t1.`user_id` = 3) as lootScore
) t

Operand should contain 1 column(s) in my sql query

Can someone help me with this query?
SELECT ((SELECT t6.`idreserva`, t5.`sala`, t6.`data`,t3.`inicio`, t4.`fim`, t6.`atividade`
FROM `req_reserva_detail` AS t1
INNER JOIN `req_material_tempo` AS t3 ON (t1.`idtempoInicio` = t3.`idtempo`)
INNER JOIN `req_material_tempo` AS t4 ON (t1.`idtempoFim` = t4.`idtempo`)
INNER JOIN `req_material_sala` AS t5 ON (t1.`idsala` = t5.`idsala`)
INNER JOIN `req_reservas` AS t6 ON (t1.`idreserva` = t6.`idreserva`)
INNER JOIN `utilizador` AS t7 ON (t6.`idutilizador` = t7.`idutilizador`) WHERE t6.`idutilizador` = 670 AND t6.`data` >= CURDATE() ORDER BY DATA ASC),
(SELECT nome FROM `req_reserva_detail` AS t1, `req_material_equipamento` AS t2 WHERE t1.`idequipamento` = t2.`idequipamento` GROUP BY nome))
It gives that error and i don't get how to fix that...
Regards
UPDATE
Schema
(SELECT t6. idreserva,
t5. sala,
t6. data,
t3. inicio,
t4. fim,
t6. atividade
FROM req_reserva_detail AS t1
INNER JOIN req_material_tempo AS t3
ON (t1. idtempoInicio = t3. idtempo)
INNER JOIN req_material_tempo AS t4
ON (t1. idtempoFim = t4. idtempo)
INNER JOIN req_material_sala AS t5
ON (t1. idsala = t5. idsala)
INNER JOIN req_reservas AS t6
ON (t1. idreserva = t6. idreserva)
INNER JOIN utilizador AS t7
ON (t6. idutilizador = t7. idutilizador)
WHERE t6. idutilizador = 670
AND t6. data >= CURDATE()
ORDER BY DATA ASC)
This subquery of yours returns 5 columns, but this is used as a subquery and mySQL expects it to return only one column

How to FULL OUTER JOIN multiple tables in MySQL

I need to FULL OUTER JOIN multiple tables. I know how to FULL OUTER JOIN two tables from here. But I have several tables, and I can't apply it over them. How can I achieve it?
My SQL code, below:
INSERT INTO table
(
customer_id
,g01
,g02
,g03
,has_card
,activity
)
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
LEFT JOIN s_category sc
ON sc.customer_id = sgd.customer_id
UNION
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
RIGHT JOIN s_category sc
ON sc.customer_id = sgd.customer_id
UNION
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
LEFT JOIN s_activity a
ON a.customer_id = sgd.customer_id
UNION
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
RIGHT JOIN s_activity a
ON a.customer_id = sgd.customer_id
Also I tried this query:
INSERT INTO reportls.table
(
customer_id
,g01
,g02
,g03
,has_card
,activity
)
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
LEFT JOIN s_category sc
ON sc.customer_id = sgd.customer_id
LEFT JOIN s_activity a
ON sc.customer_id = sgd.customer_id
UNION
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
LEFT JOIN s_category sc
ON sc.customer_id = sgd.customer_id
RIGHT JOIN s_activity a
ON a.customer_id = sgd.customer_id
UNION
SELECT sgd.customer_id, sgd.g01,sgd.g02,sgd.g03,sc.value, a.activity
FROM s_geo_data sgd
RIGHT JOIN s_category sc
ON sc.customer_id = sgd.customer_id
LEFT JOIN s_activity a
ON a.customer_id = sgd.customer_id
Last query executes very long time, I need faster query.
I think to have a FULL OUTER JOIN over 3 tables, you need to do it like this:
SELECT t1.value, t2.value, t3.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value
LEFT JOIN t3 ON t1.value = t3.value
UNION ALL
SELECT t1.value, t2.value, t3.value
FROM t2 LEFT JOIN t1 ON t1.value = t2.value
LEFT JOIN t3 ON t2.value = t3.value
WHERE t1.value IS NULL
UNION ALL
SELECT t1.value, t2.value, t3.value
FROM t3 LEFT JOIN t1 ON t1.value = t3.value
LEFT JOIN t2 ON t2.value = t3.value
WHERE t1.value IS NULL AND t2.value IS NULL
As an alternative for this:
SELECT t1.value, t2.value, t3.value
FROM t1 FULL OUTER JOIN t2 ON t1.value = t2.value
FULL OUTER JOIN t3 ON t1.value = t3.value
I suggest you to create some temporary tables like t1, t2 and t3 for storing results of your queries, then use above query over those.

Query for merging two table data

I have two tables :
stock in
Id---date---Itemname----stockInqty
1 --12/12/2014 ----testitem----12
2 --13/12/2014 ----testitem11----20
and stock out
Id--date--Itemname---stockOutqty
1 --12/12/2014 ----testitem----7
2 --14/12/2014 ----testitem11----15
I need a combined result of testitem record between two date period in the order of
date----itemname----stockInqty----stockOutqty
12/12/2014--testitem---12----7
13/12/2014--testitem---20----Nil
13/12/2014--testitem---NIL----15
You could use FULL JOIN if it was not MySQL, so you should use LEFT/RIGHT JOIN with UNION :
SELECT t1.`date`
, t1.itemname
, t1.stockInqty AS stockInqty
, t2.stockInqty AS stockOutqty
FROM tbl1 t1 LEFT JOIN tbl2 t2 ON t1.`date` = t2.`date`
AND t1.itemname = t2.itemname
UNION
SELECT t2.`date`
, t2.itemname
, t1.stockInqty AS stockInqty
, t2.stockInqty AS stockOutqty
FROM tbl1 t1 RIGHT JOIN tbl2 t2 ON t1.`date` = t2.`date`
AND t1.itemname = t2.itemname
Try this:
SELECT *
FROM StockA
LEFT OUTER JOIN StockB
ON StockA.dataItem = StockB.dataItem
UNION
SELECT *
FROM StockB
LEFT OUTER JOIN StockA
ON StockA.dataItem = StockB.dataItem
You must use a FULL OUTER JOIN operation
In MySql FULL OUTER JOIN can emulate with two LEFT OUTER JOIN in UNION

MySql Select data from multiple tables

This is my current query:
SELECT * FROM images T
JOIN boxes_items T2
ON T.ITEM_ID = T2.ITEM_PARENT_ID
WHERE T2.ITEM_ID = '$image_id'
I also need to Select all from a table named 'boxes' where the box_id is taken from boxes_items.
How to add this to the query?
try this
SELECT T.* , T2.* , T3.* FROM images T
JOIN boxes_items T2 ON T.ITEM_ID = T2.ITEM_PARENT_ID
JOIN boxes T3 ON T3.box_id = t2.box_id
WHERE T2.ITEM_ID = '$image_id'
SELECT *
FROM images T
JOIN boxes_items T2 ON T.ITEM_ID = T2.ITEM_PARENT_ID
JOIN boxes AS b ON b.box_id = t2.box_id
WHERE T2.ITEM_ID = '$image_id'
You really shouldn't use *, it can get you into more trouble than it is worth. Especially since you have more than one table in your query.
Anyway:
Select T.*, T2.*, T3.*
from images T
join boxes_items T2 on T.ITEM_ID = T2.ITEM_PARENT_ID
join boxes T3 on T3.box_id = T2.box_id
WHERE T2.ITEM_ID = '$image_id'