mysql - Referencing alias for calculation after UNION - mysql

Ok, here's the query (pseudo-query):
SELECT *, (t1.field + t2.field) as 'result', (t1.field * t2.field) as result2 from((select as t1 limit 1) UNION ALL (select as t2 limit 1))
I need both rows returned, then do the math on the two fields into the result aliases. I know it's not graceful, but I have to kludge two queries together (the first is the union, and the second is the math)
So, how do I reference and use those two inner aliases? The inner aliases aren't accessible to the outer select.
I have a suspicion there's an obvious solution here that my brain is missing.

When you union two statements together your result is a single resultset. What you'll build:
FROM
(
(SELECT f1, f2 FROM table1 LIMIT 1)
UNION
(SELECT g1, g2 FROM table2 LIMIT 1)
) derived_table_1
This will give you a single result set named derived_table_ with two fields named f1 and f2 respectively. There will be two rows, one from your first SELECT statement and another from your second. The table aliases that you assigned inside your UNION query are no longer referencable. They exist only within their own SELECT statements.
If you have a relationship between Table1 and Table2 then you want a JOIN here:
SELECT
t1.f1 + t2.g1 as result1,
t1.f2 + t2.g2 as result2,
FROM
table1 as t1
INNER JOIN table2 as t2 ON
t1.f1 = t2.g1
If, instead no relationship exists, then you are probably looking for you original, and kludgy, union with a SUM in the SELECT:
SELECT
sum(derived_table_1.f1) as result,
sum(derived_table_1.f2) as result2
FROM
(
(SELECT f1, f2 FROM table1 LIMIT 1)
UNION
(SELECT g1, g2 FROM table2 LIMIT 1)
) derived_table_1
Editted to add a SQLFIDDLE with the last example: http://sqlfiddle.com/#!2/c8707/10

The column names or aliases for the result of a UNION are always determined by the first query. The column names or aliases defined in the subsequent queries of the union are ignored.
Demo:
mysql> create table foo ( a int, b int, c int );
mysql> insert into foo values (1,2,3);
mysql> create table bar (x int, y int, z int);
mysql> insert into bar values (4,5,6);
mysql> select a, b, c from (select a, b, c from foo union select x, y, z from bar) as t;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 2 | 3 |
| 4 | 5 | 6 |
+------+------+------+
mysql> select x from (select a, b, c from foo union select x, y, z from bar) as t;
ERROR 1054 (42S22): Unknown column 'x' in 'field list'

Related

Mysql - reference an external field from a nested subquery

How is possible to reference an external field from a nested subquery?
I'll explain better very fastly with an example:
SELECT
x
, (SELECT t1.x) as x1
/*, (SELECT x FROM (SELECT t1.x) as t2) as x2*/
FROM
(SELECT 1 as x UNION SELECT 2 as x UNION SELECT 3 as x) as t1;
If I uncomment the commented subquery, I get a "Unknown table 't1' in field list" error but I need to refer for a complex calculation to that variable from a 2-level nested subquery (it's not possible to do with a Join).
This is not possible according to MySQL manual. You can try using VIEWs instead of derived tables or list all your derived tables in the outermost FROM clause
You can't use table alias in subquery, because it is out of scope. You should (eventually) recode the subquery like this:
SELECT
x,
t1.x as x1,
(SELECT x
FROM (SELECT 1 as x
UNION
SELECT 2 as x
UNION SELECT 3 as x) as t2) as x2
FROM
(SELECT 1 as x
UNION
SELECT 2 as x
UNION
SELECT 3 as x) as t1;
or create a proper view and use the view for access to the data
create view my_view as
SELECT 1 as x
UNION
SELECT 2
UNION
SELECT 3
;
SELECT x
, t1.x as x1
, (SELECT x FROM my_view as t2) as x2
FROM my_view as t1
;

MySQL - Finding how much duplicates are inside the same table given

Considering I have the following two sets of rows (same type) in a WHERE clause:
A B
1 1
2 2
3 4
I need to find how many A is in B
For example, for the given table above, it would be 66% since 2 out of 3 numbers are in B
Another example:
A B
1 1
2 2
3 4
5
3
Would give 100% since all of the numbers in A are in B
Here is what I tried myself: (Doesn't work on all test cases..)
DROP PROCEDURE IF EXISTS getProductsByDate;
DELIMITER //
CREATE PROCEDURE getProductsByDate (IN d_given date)
BEGIN
SELECT
Product,
COUNT(*) AS 'total Number',
(SELECT
(SELECT COUNT(DISTINCT Part) FROM products WHERE Product=B.Product) - COUNT(*)
FROM
products AS b2
WHERE
b2.SOP < B.SOP AND b2.Part != B.Part) AS 'New Parts',
CONCAT(round((SELECT
(SELECT COUNT(DISTINCT Part) FROM products WHERE Product=B.Product) - COUNT(*)
FROM
products AS b2
WHERE
b2.SOP < B.SOP AND b2.Part != B.Part)/count(DISTINCT part)*100, 0), '%') as 'Share New'
FROM
products AS B
WHERE
b.SOP < d_given
GROUP BY Product;
END//
DELIMITER ;
CALL getProductsByDate (date("2018-01-01"));
Thanks.
Naming your tables TA and TB respectively you could try something like this (test made on MSSQL and Mysql at moment)
SELECT ROUND(SUM(PERC) ,4)AS PERC_TOT
FROM (
SELECT DISTINCT TA.ID , 1.00/ (SELECT COUNT(DISTINCT ID) FROM TA) AS PERC
FROM TA
WHERE EXISTS ( SELECT DISTINCT ID FROM TB WHERE TB.ID=TA.ID)
) C;
Output with your first sample data set:
PERC_TOT
0,6667
Output with your second sample data set:
PERC_TOT
1,0000
Update (I made the original for two tables, as I was thinking at solution). This is for one single table (is almost the same than the former query): (I used ID1 for column A and ID2 for column B)
SELECT ROUND(SUM(PERC) ,4)AS PERC_TOT
FROM (
SELECT DISTINCT TA.ID1 , 1.00/ (SELECT COUNT(DISTINCT ID1) FROM TA) AS PERC
FROM TA
WHERE EXISTS ( SELECT DISTINCT ID2 FROM TA AS TB WHERE TB.ID2=TA.ID1)
) C;

Semi-distinct union

There are UNION and UNION ALL operators in SQL. The first one drops all the duplicates, the second one doesn't. I want to drop only duplicates which originated in different subqueries, but keep those which come from the same one. Example:
TABLE t1: TABLE t2: TABLE t3:
a | b a | b a | b
————— ————— —————
1 | 2 1 | 2 1 | 3
1 | 2 1 | 3
I want (SELECT * FROM t1) UNION SEMI (SELECT * FROM t3) to return all four rows,
then (SELECT * FROM t2) UNION SEMI (SELECT * FROM t2) to return one row.
I don't really care what
(SELECT * FROM t1) UNION SEMI (SELECT * FROM t2) would return, but it would be nice if that somehow depended one the order of subqueries, e. g. in the last example it would be two rows, and in reversed (t2 UNION t1) — one.
I can do it with a huge query, but the question is — is there a standard method for such operation?
Thanks in advance.
You want a union of table1 with all the rows in table2 that don't have matching rows in table1
SELECT *
FROM table1
UNION ALL
SELECT t2.*
FROM table2 t2
LEFT JOIN table1 t1 USING (a, b, ...) -- list all the columns here
WHERE t1.a IS NULL

SELECT * FROM Table WHERE colA and colB has at least 3 identical

I have to combine these two mySQL queries into one. I duplicated a solution and used it on a join table. I am querying a join table that has two columns (labeled "to_" and "from_"). Both 'to_' and 'from_' hold an id number for the same table. I need to combine these queries in such a way that I get results based on: [('from_' + 'to_') > 3], where 'from_' and 'to_' have the same value (i.e., they refer to the same id).
$query = "select * from nodes where nodeID in (
select to_ from joinTable group by to_ having count(*) > 3
)";
...
$query = "select * from nodes where nodeID in (
select from_ from joinTable group by from_ having count(*) > 3
)";
Acknowledgement: I'm using a query based very closely on a solution 'Mr E' helped me with earlier.
You can try (see important notice at the last paragraph regarding to_ and from_ matching requirements):
SELECT X.to_, COUNT(*)
FROM joinTable X, joinTable Y
WHERE
X.to_ = Y.from_
GROUP BY X.to_
HAVING COUNT(*) > 2
Or
SELECT X.to_, COUNT(*)
FROM joinTable X LEFT JOIN joinTable Y ON X.to_ = Y.from_
GROUP BY X.to_
HAVING COUNT(*) > 2
Using Mr E's test data:
CREATE TABLE `foo` (
`id` int(1) DEFAULT NULL,
`to_` varchar(5) DEFAULT NULL,
`from_` varchar(5) DEFAULT NULL
);
INSERT INTO `foo` VALUES (1,'A','B'),(2,'B','A'),(3,'B','C'),(4,'X','C'),(4,'X','B');
It will work, half-way, by issuing:
SELECT X.to_, Y.from_
FROM foo X LEFT JOIN foo Y ON X.to_ = Y.from_
which will then yield:
mysql> SELECT X.to_, Y.from_ /*--, COUNT(*) */
-> FROM foo X LEFT JOIN foo Y ON X.to_ = Y.from_;
+------+-------+
| to_ | from_ |
+------+-------+
| A | A |
| B | B |
| B | B |
| B | B |
| B | B |
| X | NULL |
| X | NULL |
+------+-------+
7 rows in set (0.00 sec)
and by running in full:
mysql> SELECT X.to_, COUNT(*)
-> FROM foo X LEFT JOIN foo Y ON X.to_ = Y.from_
-> GROUP BY X.to_
-> HAVING COUNT(*) > 2;
+------+----------+
| to_ | COUNT(*) |
+------+----------+
| B | 4 |
+------+----------+
Basically, join the table with itself and then generate an N:N list of matching records from both tables where to_ and from_ match (whether or not on the same row), then work with a single column and aggregate its values for the final COUNT(*).
And, most importantly, why have I lowered the number on the HAVING COUNT(*) from 3 to 2? The N:N relationship will issue N1 * N2 records (where N1 is the count of matching records on the first table and N2 on the second). So if the lower bound is three, we can only have over 3 records on these two tables by having one record in one of them and then 3 on the other (in whatever order) or 2 in one and 2 on the other (and then up from there) - otherwise there will be no matches on the to_ and from_ fields and this is something I am not sure about - whether the OP wants only records whose values appear on both fields or if having a COUNT(*) from a single side would suffice. If the latter is the case, however, I don't see any other option apart from separating the queries to deal with each column individually, as some people already have posted since that's an isolated sum we're dealing with. This will be slow if running against large tables.
SELECT * FROM(
SELECT nodeID, to_, count(*) cto_ FROM joinTable jta GROUP BY to_
OUTER JOIN
SELECT from_, count(*) cfrom_ FROM joinTable jtb GROUP BY from_
ON jta.nodeID = jtb.nodeID
) WHERE ((cto_ + cfrom) > 3) as tableA
INNER JOIN
node
ON node.nodeID = tableA.nodeID
I haven't tested to make sure this code compiles and runs but I think that's generally the right direction for the answer to what you want--
first get the count of to's from the to table
then get the count of from's from the from table
finally put the addition in the criteria for the two tables.
As long as the outer join is on same nodeID's it should only have one entry per nodeID, if I understand my code right.
OK, I ran it through a database I had handy, here's actual code that works on my database (change the names for yours of course)
SELECT * FROM (
SELECT * FROM
(
SELECT ticket_id, author_uid, count(*) cto_
FROM strato_ticket GROUP BY author_uid
) as jta
LEFT JOIN
(
SELECT ticket_id as tid, uid, count(*) cfrom_
FROM strato_ticket GROUP BY uid
) as jtb
ON jta.ticket_id = jtb.tid
WHERE ((cto_ + cfrom_) > 3)
) as jt
INNER JOIN strato_invite
ON strato_invite.ticket_id = jt.tid
It's not pretty, but:
select * from nodes where nodeID in (
select x from (
select to_ as x, count(*) as num
from joinTable group by to_
union all
select from_ as x, count(*) as num
from joinTable group by from_
) as temp_table
group by x having sum(num) > 3;
)
Doesn't seem to work for the OP. All I can say is "works on my machine" - here's the data and exact query I used:
CREATE TABLE `foo` (
`id` int(1) DEFAULT NULL,
`to_` varchar(5) DEFAULT NULL,
`from_` varchar(5) DEFAULT NULL
);
INSERT INTO `foo` VALUES (1,'A','B'),(2,'B','A'),(3,'B','C'),(4,'X','C'),(4,'X','B');
The query:
select x from (
select to_ as x, count(*) as num
from foo group by to_
union all
select from_ as x, count(*) as num
from foo group by from_
) as temp_table
group by x having sum(num) > 3;

find the output mysql query

I have a table name T1 having only one Column name Col1 having rows –
Col1
a
b
c
And another table name T2 also having only one Column name Col1 having rows –
Col1
x
y
z
Now I want record like
Col1--Col2
a------x
b------y
c------z
I am using mysql.
Thanks in advance!!
create table T1(col1 varchar(10));
insert T1 values ('a'),('b'),('c');
create table T2(col2 varchar(10));
insert T2 values ('x'),('y'),('z');
select A.col1, B.col2 from
(select #r:=#r+1 rownum, col1 from (select #r:=0) initvar, T1) A,
(select #s:=#s+1 rownum, col2 from (select #s:=0) initvar, T2) B
where A.rownum=B.rownum
Because there is no ORDER BY clause, you are depending on luck and convention for the row numbering to be according to the order inserted. It may not always be the case.
In your example, if you want to join the tables to get row results like this:
Row 1 - A,X
Row 2 - B,Y
Row 3 - C,Z
..then you will have to add a common field that you can JOIN the two tables on.
If you want to be able to return results from both tables like this:
Row 1 - A
Row 2 - B
Row 3 - C
Row 4 - X
Row 5 - Y
Row 6 - Z
.. then you will need to use a UNION:
(SELECT Col1 FROM T1) UNION (SELECT Col1 FROM T2)