Is there a way to combine two SELECT statements on two disjoint tables (t1, t1) into once SELECT statement? The ideal query statement would return results from both tables when successful or the results if only the t1 query part is successful or the results if only the t2 query part is successful or nothing if the query on t1 and t2 is unsuccessful.
MySQL UNION doesn't work because the tables are disjoint. JOIN doesn't appear to work because if the query fails for one table the entire query fails.
Test case:
create table t1 (
c11 varchar(2),
c12 varchar(2),
c13 varchar(2),
c14 varchar(2),
primary key (c11));
create table t2 (
c21 varchar(2),
c22 varchar(2),
c23 varchar(2),
primary key(c21));
insert into t1 values ('a', 'b', 'c', 'd');
insert into t2 values ('x', 'y', 'z');
Example of what the two distinct SELECT statements:
SELECT c11, c12, c13, c14 from t1 where c11 = 'a'
Returns a, b, c, d
SELECT c21, c22, c23 from t2 where c21 = 'x'
Returns x, y, z
Examples of what I am trying to achieve:
SELECT * (successful query of t1 and t2) where t1.c11 = 'a' and t2.c21 = 'x'
Returns a, b, c, d, x, y, z
SELECT * (successful query of only t1 and not t2) where t1.c11 = 'a' and t2.c21 = 'v'
Returns a, b, c, d
SELECT (successful query of only t2 and not t1) where t1.c11 = 'd' and t2.c21 = 'x'
Returns x, y, z
SELECT (unsuccessful query of both t1 and t2) where t1.c11 = 'd' and t2.c21 = 'v'
Empty set.
You can just join the tables on a condition which always evaluates true, e.g.
select c11,c12,c13,c14,c21,c22,c23 from t1
left join t2 on true
where c21='x' and c11='a';
Result:
Kinda hard for me to understand why you want to do this, but there you go. Note that, without the where clause, this query will give as many results as there are rows in t1 multiplied by the number of rows in t2. So if they both have 10 rows, an unconditional query would return 100 results.
Related
Cont. with this post, a function is created to parse json input then insert values into three tables, with previous inserted ids as parameter for last
insert.
If i want to insert two arrays into the same table, i can just do
insert into t2 (car, car_type)
select json_array_elements_text(d::json -> 'car'),json_array_elements_text(d::json -> 'car_type')::int4 returning id;
)
how to make it work with index as below?
function:
create or replace function test_func(d json)
returns void as $$
begin
with j as (select d)
, a as (
select car,brand,type, t1.id oid
from j
join json_array_elements_text(j.d->'cars') with ordinality t1(car,id) on true
join json_array_elements_text(j.d->'brands') with ordinality t2(brand,id)
on t1.id = t2.id
join json_array_elements_text(j.d->'car_type') with ordinality t2(type,id)
on t1.id = t2.id // this line apparently doesnt work, t2 has been joined twice
)
, n as (
insert into t1 (name) values (d::json -> 'name') returning id
), c as (
insert into t2 (cars,car_type) select car,type from a order by oid returning id // needs to insert two columns here from two arrays
)
, ag as (
select array_agg(c.id) cid from c
)
insert into t3 (id, name_id, cars_id, brand)
select 1, n.id,cid[oid], brand
from a
join n on true
join ag on true
;
end;
$$ language plpgsql;
Tables:
CREATE TABLE t1 ( "id" SERIAL PRIMARY KEY, "name" text NOT NULL );
CREATE TABLE t2 ( "id" SERIAL PRIMARY KEY, "cars" text NOT NULL, "car_type" int );
CREATE TABLE t3 ( "id" int, "name_id" int REFERENCES t1(id), "cars_id" int REFERENCES t2(id), "brand" text );
Test:
select test_func('{"name":"john", "cars":["bmw X5 xdrive","volvo v90 rdesign"], "brands":["bmw","volvo"],"car_type":[1,1]}');
you used t2 for aliasing two different sets - try:
create or replace function test_func(d json)
returns void as $$
begin
with j as (select d)
, a as (
select car,brand,car_type, t1.id oid
from j
join json_array_elements_text(j.d->'cars') with ordinality t1(car,id) on true
join json_array_elements_text(j.d->'brands') with ordinality t2(brand,id)
on t1.id = t2.id
join json_array_elements_text(j.d->'car_type') with ordinality car_t(car_type,id)
on t1.id = car_t.id
)
, n as (
insert into t1 (name) values (d::json -> 'name') returning id
), c as (
insert into t2 (cars,car_type) select car,car_type::int from a order by oid returning id -- needs to insert two columns here from two arrays
)
, ag as (
select array_agg(c.id) cid from c
)
insert into t3 (id, name_id, cars_id, brand)
select 1, n.id,cid[oid], brand
from a
join n on true
join ag on true
;
end;
$$ language plpgsql;
result:
t=# select * from t2;
id | cars | car_type
----+-------------------+----------
1 | bmw X5 xdrive | 1
2 | volvo v90 rdesign | 1
(2 rows)
I'm not sure it's possible.
I have a table with 3 columns, and I want to get different values of each one, there is an example:
A B C
--------------------
a1 b1 c1
a1 b2 c2
a1 b3 c1
The expected result is:
A B C
-----------------
a1 b1 c1
b2 c2
b3
So, it will be the union of:
SELECT DISTINCT A FROM myTable
SELECT DISTINCT B FROM myTable
SELECT DISTINCT C FROM myTable
Using UNION I get the result in one column.
Is this posible to do?
Please try the following...
SELECT COALESCE( a, '' ) AS a,
COALESCE( b, '' ) AS b,
COALESCE( c, '' ) AS c
FROM ( SELECT a,
ROW_NUMBER() OVER ( ORDER BY a ) AS recordNumber
FROM ( SELECT a
FROM myTable
GROUP BY a
) aFinder
) aNumberedFinder
FULL JOIN ( SELECT b,
ROW_NUMBER() OVER ( ORDER BY b ) AS recordNumber
FROM ( SELECT b
FROM myTable
GROUP BY b
) bFinder
) bNumberedFinder ON bNumberedFinder.recordNumber = aNumberedFinder.recordNumber
FULL JOIN ( SELECT c,
ROW_NUMBER() OVER ( ORDER BY c ) AS recordNumber
FROM ( SELECT c
FROM myTable
GROUP BY c
) cFinder
) cNumberedFinder ON cNumberedFinder.recordNumber = aNumberedFinder.recordNumber
OR cNumberedFinder.recordNumber = bNumberedFinder.recordNumber;
This statement starts with the following subquery...
SELECT a
FROM myTable
GROUP BY a
This subquery produces a list of the unique values in field a. The following subquery reproduces this list with record (aka row) numbers...
SELECT a,
ROW_NUMBER() OVER ( ORDER BY a ) AS recordNumber
FROM ( SELECT a
FROM myTable
GROUP BY a
) aFinder
Similar subqueries are used to produce numbered lists of the unique values in fields b and c.
A FULL OUTER JOIN (abbreviated here to FULL JOIN) is then performed between the lists for a and b, with their record numbers being used as the common / joining value.
Note : An INNER JOIN would only have returned as many records as there are in the shorter list of the two. A LEFT JOIN would only have been effective if the list on the left of the JOIN were of greater or longer length than the list on the right. Similar logic applies to the usage of a RIGHT JOIN. A FULL JOIN will join the two lists in the manner of a LEFT JOIN or RIGHT JOIN irrespective of which list is longer.
A FULL JOIN is then performed between the above joined dataset and the list for c where it finds a common value in either list.
The values of a, b and c are then selected from the finally joined dataset, with the COALESCE() function replacing the occurrence of any NULL values, such as those generated by the joining process, with the empty string ('')
If you have any questions or comments, then please feel free to post a Comment accordingly.
Appendix
My statement was tested against a database created using the following script...
CREATE TABLE myTable
(
a VARCHAR( 5 ),
b VARCHAR( 5 ),
c VARCHAR( 5 )
);
INSERT INTO myTable ( a,
b,
c
)
VALUES ( 'a1', 'b1', 'c1' ),
( 'a1', 'b2', 'c2' ),
( 'a1', 'b3', 'c1' );
Further Reading
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/coalesce-transact-sql (on using the COALESCE() function in Transact-SQL)
https://www.w3schools.com/sql/sql_join.asp (on the various types of horizontal JOIN - the Venn diagram is useful)
https://technet.microsoft.com/en-us/library/ms187518(v=sql.105).aspx (on using OUTER JOIN in SQL-Server)
https://learn.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql (on using ROW_NUMBER() in Transact-SQL)
1.The following query returns a value:
select x , y,(x/cast(y as float)*100) from
(select count(distinct t1.ST_NUM) as x
from table2 t2,
table1 t1,
table t3
where
t2.CD in ($CD1)
and t2.ITM_CD=($ITM_CD)
and t2.div=($div)
and t2.div= t1.div
and t2.scn_cd = t1._scn_cd
and t1.week_end =t3.week_end
and t3.week_end between ($startdate_1) and ($enddate_1))a1
cross join
(select count(distinct t1.ST_NUM) as y
from from table2 t2,
table1 t1,
table t3
where
t2.CD in ($CD1)
and t2.ITM_CD=($ITM_CD)
and t2.div=($div)
and t2.div= t1.div
and t2.scn_cd = t1._scn_cd
and t1.week_end =t3.week_end
and t3.week_end between ($startdate_2) and ($enddate_2))a2
2.The value that is returned by the above query has to be used as follows
If value Between a and b do this
If value between d and c do this
I understand that I have to use a case statement. I am not sure how to pass the value as input to the new sql script that I am going to write
Thanks for your help
Modify the beginning of your query as follows:
DECLARE #x int
DECLARE #y int
DECLARE #z float
select #x=x , #y=y, #z=(x/cast(y as float)*100) from
{rest of query unchanged}
You can now reference #x, #y, and #z in any subsequent SQL statements within the same query. If you need to be able to access them from a completely different query then you will need to store them somewhere more permanent like a table.
Just write PL/SQL block:
begin
for rec in (
select x , y,(x/cast(y as float)*100) as z from
(select count(distinct t1.ST_NUM) as x
from table2 t2,
table1 t1,
table t3
where
t2.CD in ($CD1)
and t2.ITM_CD=($ITM_CD)
and t2.div=($div)
and t2.div= t1.div
and t2.scn_cd = t1._scn_cd
and t1.week_end =t3.week_end
and t3.week_end between ($startdate_1) and ($enddate_1))a1
cross join
(select count(distinct t1.ST_NUM) as y
from from table2 t2,
table1 t1,
table t3
where
t2.CD in ($CD1)
and t2.ITM_CD=($ITM_CD)
and t2.div=($div)
and t2.div= t1.div
and t2.scn_cd = t1._scn_cd
and t1.week_end =t3.week_end
and t3.week_end between ($startdate_2) and ($enddate_2))a2
) loop
-- do here your if/case logic. Something like this:
case
when rec.z between a and b then ....
when rec.z between c and d then ....
end case;
end loop;
end;
/
MySQL How do you INSERT INTO a table with a SELECT subquery returning multiple rows?
INSERT INTO Results
(
People,
names,
)
VALUES
(
(
SELECT d.id
FROM Names f
JOIN People d ON d.id = f.id
),
(
"Henry"
),
);
I WANT to populate the new table with all results returning from this subquery. How do I do this without getting a ERROR 1242 (21000): Subquery returns more than 1 row
INSERT INTO Results (People, names )
SELECT d.id, 'Henry'
FROM Names f
JOIN People d ON d.id = f.id
Combine the static string Henry with your SELECT query.
Here is what I've found that works well. It is a little long but many times extra data needs to be shuffled around.
Insert multiple rows into table1 from table2 with values.
EXAMPLES:
INSERT INTO table1 (col1, col2, col3, col4, col5)
SELECT col1,col2,col3,col4,col5
FROM table2 t2
WHERE t2.val2 IN (MULTIPLE VALUES)
AND (Another Conditional);
You can insert hard coded values to get insert multiple rows with repeat data:
INSERT INTO table1 (col1, col2, col3, col4, col5)
SELECT "Value", col2, col3, "1900-01-01","9999-12-31"
FROM table2 t2
WHERE t2.val2 IN (MULTIPLE VALUES)
AND (Another Conditional);
Note that: "Value","1900-01-01","9999-12-31" will repeat across all rows inserted.
INSERT INTO Results
(
People,
names,
)
SELECT d.id, 'Henry'
FROM Names f
JOIN People d ON d.id = f.id
INSERT INTO Results
(
People,
names,
)
VALUES
(
(
SELECT d.id
FROM Names f
JOIN People d ON (d.id = f.id) limit 1
),
(
"Henry"
),
);
The reason of this error (Subquery returns more than 1 row) is that you use parenthesis (). Look more careful to the best answer. It doesn't contain parethesis around subquery
In MySql multiple values from strings can be inserted like the following avoiding duplicates. Thanks.
insert into brand(name) select * from (
select 'Fender' as name
union select 'a'
union ..... ) t
where not exists (select 1 from brand t2 where t2.name COLLATE latin1_general_ci = t.name COLLATE utf8mb4_unicode_ci )
insert into ec_element(parentid,name) select elementid , 'STARTUP' from ec_element where name = 'BG';
insert statement takes values elementid from the table found with condition fulfilled and a label string.
Long time user, first time poster. I've found similar questions/answers, typically involving subqueries, but I'm not sure how to apply to my situation.
I have 3 tables:
table1
id
table2
id | val (each id has 1 of 3 possible values)
table3
id | val (each id has 1 of 3 possible values)
EDIT: Example: (table1 = unique id of everyone who attended a theme park; table2 = which attraction each visitor visited first; table3 = which attraction each visitor visited second).
I want to write a query to look up 7 different counts:
(1) count of the unique ids in table1
(2) count of the number of ids that have each of the possible values in table2
(3) count of the number of ids that have each of the possible values in table3
My MySQL query:
SELECT
count(DISTINCT table1.id) AS x1,
SUM(IF(table2.val='1'),1,0)) AS x2,
SUM(IF(table2.val='2'),1,0)) AS x3,
SUM(IF(table2.val='3'),1,0)) AS x4,
SUM(IF(table3.val='1'),1,0)) AS x5,
SUM(IF(table3.val='2'),1,0)) AS x6,
SUM(IF(table3.val='3'),1,0)) AS x7
FROM
table1
LEFT JOIN
table2 ON table1.id=table2.id
LEFT JOIN
table3 ON table1.id=table3.id
Results:
x1 = correct (because of DISTINCT)
x2,x3,x4 = correct
x5,x6,x7 = TWICE the number they should be (because I'm getting cartesian product?)
Any suggestions?
You are getting a Cartesian result. Since you are not showing how many "1", "2" or "3" counts per "ID", just do a select sum() from those tables by themselves. Since a sum with no group by will always result in ONE record, you don't need any join and it will pull the results of one record per each summary with no Cartesian result. Since your original query was LEFT JOIN to the others, the ID would have already existed on table 1, so why re-query count distinct in each sub-table.
SELECT
SumForTable1.x1,
SumForTable2.x2,
SumForTable2.x3,
SumForTable2.x4,
SumForTable3.x5,
SumForTable3.x6,
SumForTable3.x7
FROM
( select count(DISTINCT table1.id) AS x1
from table1 ) SumForTable1,
( select SUM(IF(table2.val='1'), 1, 0)) AS x2,
SUM(IF(table2.val='2'), 1, 0)) AS x3,
SUM(IF(table2.val='3'), 1, 0)) AS x4
from table2 ) SumForTable2,
( select SUM(IF(table3.val='1'), 1, 0)) AS x5,
SUM(IF(table3.val='2'), 1, 0)) AS x6,
SUM(IF(table3.val='3'), 1, 0)) AS x7
from table3 ) SumForTable3
My guess is you issue is that id is not unique in table1. So even though it is unique in table2/3 (according to your description) each row in table2/3 is joined to two rows in table1 and thus counted twice. Has nothing to do with the left joins, normal inner joins would have the same issue.
If mysql (which I don't know real well) lets you do inline views like oracle does, then you can fix it by writing your query as:
SELECT
count(view1.id) AS x1,
SUM(IF(table2.val='1'),1,0)) AS x2,
SUM(IF(table2.val='2'),1,0)) AS x3,
SUM(IF(table2.val='3'),1,0)) AS x4,
SUM(IF(table3.val='1'),1,0)) AS x5,
SUM(IF(table3.val='2'),1,0)) AS x6,
SUM(IF(table3.val='3'),1,0)) AS x7
FROM
( SELECT DISTINCT table1.id
FROM table1
) view1
LEFT JOIN
table2 ON view1.id=table2.id
LEFT JOIN
table3 ON view1.id=table3.id
I'd remove duplicates on every table:
SELECT
count(t1.id) AS t1,
SUM(IF(t2.val=1,1,0)) AS t21,
SUM(IF(t2.val=2,1,0)) AS t22,
SUM(IF(t2.val=3,1,0)) AS t23,
SUM(IF(t3.val=1,1,0)) AS t31,
SUM(IF(t3.val=2,1,0)) AS t32,
SUM(IF(t3.val=3,1,0)) AS t33
FROM (SELECT DISTINCT * FROM table1) as t1
JOIN (SELECT DISTINCT * FROM table2) as t2 ON t1.id=t2.id
JOIN (SELECT DISTINCT * FROM table3) as t3 ON t1.id=t3.id;