I'm inserting a large number of rows into Table_A. Table_A includes a B_ID column which points to Table_B.B_ID.
Table B has just two columns: Table_B.B_ID (the primary key) and Table_B.Name.
I know the value for every Table_A field I'm inserting except B_ID. I only know the corresponding Table_B.Name. So how can I insert multiple rows into Table_A?
Here's a pseudocode version of what I want to do:
REPLACE INTO Table_A (Table_A.A_ID, Table_A.Field, Table_A.B_ID) VALUES
(1, 'foo', [SELECT B_ID FROM Table_B WHERE Table_B.Name = 'A'),
(2, 'bar', [SELECT B_ID FROM Table_B WHERE Table_B.Name = 'B'),...etc
I've had to do things like this when deploying scripts to a production environment where Ids differed in environments. Otherwise it's probably easier to type out the ID's
REPLACE INTO table_a (table_a.a_id, table_a.field, table_a.b_id)
SELECT 1, 'foo', b_id, FROM table_b WHERE name = 'A'
UNION ALL SELECT 2, 'bar', b_id, FROM table_b WHERE name = 'B'
If the values:
(1, 'foo', 'A'),
(2, 'bar', 'B'),
come from a (SELECT ...)
you can use this:
INSERT INTO Table_A
( A_ID, Fld, B_ID)
SELECT Data.A_ID
, Data.Field
, Table_B.B_ID
FROM (SELECT ...) As Data
JOIN Table_B
ON Table_B.Name = Data.Name
If not, you can insert them into a temporary table and then use the above, replacing (SELECT ...) with TemporaryTable.
CREATE TABLE HelpTable
( A_ID int
, Fld varchar(200)
, Name varchar(200)
) ;
INSERT INTO HelpTable
VALUES
(1, 'foo', 'A'),
(2, 'bar', 'B'), etc...
;
INSERT INTO Table_A
( A_ID, Field, B_ID)
SELECT HelpTable.A_ID
, HelpTable.Fld
, Table_B.B_ID
FROM HelpTable
JOIN Table_B
ON Table_B.Name = HelpTable.Name
;
DROP TABLE HelpTable ;
Related
I have two tables, for example 1st has id, and name.
2nd has id, link to 1st table by id and COST.
CREATE TABLE FIRST_TABLE (id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR (100));
CREATE TABLE SECOND_TABLE (id INT PRIMARY KEY, FIRST_TABLE_ID INT NOT NULL, cost DECIMAL(10,2),
FOREIGN KEY (FIRST_TABLE_ID) REFERENCES FIRST_TABLE (ID));
INSERT INTO FIRST_TABLE (NAME) VALUES
('ONE'),
('TWO'),
('THREE');
INSERT INTO SECOND_TABLE (ID, FIRST_TABLE_ID, COST) VALUES
(1, 1, 500),
(2, 2, 400),
(3, 3, 150),
(4, 1, 500),
(5, 2, 400),
(6, 3, 150);
How to get sum of elements COST (of 2nd table), which depends on NAME (of 1st table)?
What i tried to do:
select FIRST_TABLE.NAME, sum(SECOND_TABLE.COST) TOTAL_COST
from FIRST_TABLE
left join SECOND_TABLE on FIRST_TABLE_ID = SECOND_TABLE.ID
group by FIRST_TABLE.ID
The problem is:
I have only irregular sum of cost - 1050 for every NAME.
ONE - 1050
TWO - 1050
THREE - 1050
How to get genuine values for every NAME?
And how will it look like if i have three tables and for key in 1st i have to get sum of 2nd table and 3rd table?
Here:
from FIRST_TABLE
left join SECOND_TABLE on FIRST_TABLE_ID = SECOND_TABLE.ID
The join condition is actually equivalent to:
on SECOND_TABLE.FIRST_TABLE_ID = SECOND_TABLE.ID
Both operands of the equality relate to the same table. This is not what you want. Instead, use:
select FIRST_TABLE.NAME, sum(SECOND_TABLE.COST) TOTAL_COST
from FIRST_TABLE
left join SECOND_TABLE on SECOND_TABLE.FIRST_TABLE_ID = FIRST_TABLE.ID
group by FIRST_TABLE.ID
I would also recommend using table aliases to shorten the query and make it more readable:
select t1.NAME, sum(t2.COST) TOTAL_COST
from FIRST_TABLE t1
left join SECOND_TABLE t2 on t2.FIRST_TABLE_ID = t1.ID
group by t1.ID
I'm trying to join two tables. Where table2 has duplates.
The tables look something like
CREATE TABLE ta
(
id int,
cno varchar(30),
d1 varchar(30),
d2 int
);
CREATE TABLE tb
(
id int,
cno varchar(30),
cn1 varchar(30),
cn2 int
);
INSERT INTO ta
(id, cno, d1, d2)
VALUES
(1, '1234','a',2),
(2, '6456','j',3),
(3, '5456','h',4),
(4, '4454','g',5);
INSERT INTO tb
(id, cno, cn1, cn2)
VALUES
(1, '1234', 'a', 21),
(1, '1234', 'a', 22),
(2, '6456', 'b', 33),
(2, '6456', 'c', 34),
(2, '6456', 'c', 35),
(3, '5456', 'c', 36),
(4, '4454', 'c', 37);
I was able to get the result http://sqlfiddle.com/#!2/b282e3/1 in MySQL. However when I run it in Postgresql I get an error http://sqlfiddle.com/#!15/b282e/4
Output should be like http://sqlfiddle.com/#!2/b282e3/1
CNO CN1 CN2 D1 D2
1234 a 21 a 2
4454 c 37 g 5
5456 c 36 h 4
6456 b 33 j 3
Any alternatives for this in Psql?
Use aggregate functions for columns that are not used in GROUP BY:
select t2.cno,
min(t2.cn1) as a,
min(t2.cn2) as b,
min(t1.d1) as c,
min(t1.d2) as d
from ta as t1
inner join tb as t2
on t1.cno=t2.cno
group by t2.cno
http://sqlfiddle.com/#!15/b282e/23
This query in MySQL:
select t2.cno, t2.cn1, t2.cn2, t1.d1, t1.d2
from ta t1 inner join
tb t2
on t1.cno = t2.cno
group by t2.cno;
Is not valid SQL (according to the standard or other databases). The problem is that there are columns in the select that are neither in the group by nor are they arguments to aggregation functions (and they are not "functionally dependent" either). Your use of the group by extension in MySQL is officially discouraged. You can read the documentation about it here.
Ironically, Postgres has an extension called distinct on that does something similar. The syntax is:
select distinct on (t2.cno) t2.cno, t2.cn1, t2.cn2, t1.d1, t1.d2
from ta t1 inner join
tb t2
on t1.cno = t2.cno
order by t2.cno;
distinct on takes a list in parentheses and returns one row per value in the parentheses -- taking the first row and ignoring the rest. These columns need to match the columns in the order by, otherwise Postgres generates a compile-time error.
In most other databases, you would do something similar using row_number(). And you can use that as well in Postgres.
select t2.cno, min (t2.cn1), min(t2.cn2), t1.d1 , t1.d2
from ta as t1
inner join tb as t2 on t1.cno=t2.cno
group by t2.cno, t1.d1 , t1.d2
WITH Queries (Common Table Expressions)
with cte as
(
select cno,cn1,cn2 from tb where cn2 in (select min(cn2) from tb group by cno)
),
cte1 as
(
select d1,d2,cno from ta where cno in (select cno from tb where cn2 in (select
min(cn2) from tb group by cno))
)
select cte.cno,cn1,cn2,d1,d2 from cte inner join cte1 on cte1.cno = cte.cno order
by cte.cno
I am inserting data into a database that looks like this:
(1, 'blue'), (2,'large'), (3, 'round')
The numbers there correspond to ID's from another table. that looks like: id | value
When inserting this data I want to insert the actual value that the number corresponds to, not the id.
Is there any query to do this? or do I need match the values before sending it to the database?
While I know it won't work, I am hoping there is something like:
insert into table2 (table1.value[id=1], 'blue'), (table1.value[id=2],'large'), (table1.value[id=3], 'round') join table1
I imagine I could use:
insert into table2
((select value from table1 where id=1), 'blue'),
((select value from table1 where id=2),'large'),
((select value from table1 where id=3), 'round')
But with say, 40 different attributes that would make 41 queries!
First virtually make up a table with the values you want to insert (id,value), then join the derived table to table1 and INSERT the result into table2.
insert into table2
select t.value, madeup.other
from (select 1 id, 'blue' other union all
select 2, 'large' union all
select 3, 'round') madeup
join table1 t on t.id = madeup.id;
You could use a temporary table to map id to value. I don't really speak MySQL, but something like this:
create table #mapping (id int, description varchar)
insert into #mapping values (1, 'blue')
insert into #mapping values (2, 'large')
insert into #mapping values (3, 'round')
insert into table2
select table1.value, #mapping.description
from #mapping
join table1 on table1.id = #mapping.id
drop table #mapping
Suppose I have these tables
create table bug (
id int primary key,
name varchar(20)
)
create table blocking (
pk int primary key,
id int,
name varchar(20)
)
insert into bug values (1, 'bad name')
insert into bug values (2, 'bad condition')
insert into bug values (3, 'about box')
insert into blocking values (0, 1, 'qa bug')
insert into blocking values (1, 1, 'doc bug')
insert into blocking values (2, 2, 'doc bug')
and I'd like to join the tables on id columns and the result should be like this:
id name blockingName
----------- -------------------- --------------------
1 bad name qa bug
2 bad condition NULL
3 about box NULL
This means:
I'd like to return all rows from #bug
there should be only 'qa bug' value in column 'blockingName' or NULL (if no matching row in #blocking was found)
My naive select was like this:
select * from #bug t1
left join #blocking t2 on t1.id = t2.id
where t2.name is null or t2.name = 'qa bug'
but this does not work, because it seems that the condition is first applied to #blocking table and then it is joined.
What is the simplest/typical solution for this problem? (I have a solution with nested select, but I hope there is something better)
Simply put the "qa bug" criteria in the join:
select t1.*, t2.name from #bug t1
left join #blocking t2 on t1.id = t2.id AND t2.name = 'qa bug'
correct select is:
create table bug (
id int primary key,
name varchar(20)
)
insert into bug values (1, 'bad name')
insert into bug values (2, 'bad condition')
insert into bug values (3, 'about box')
CREATE TABLE blocking
(
pk int IDENTITY(1,1)PRIMARY KEY ,
id int,
name varchar(20)
)
insert into blocking values (1, 'qa bug')
insert into blocking values (1, 'doc bug')
insert into blocking values (2, 'doc bug')
select
t1.id, t1.name,
(select b.name from blocking b where b.id=t1.id and b.name='qa bug')
from bug t1
It looks like you want to select only one row from #blocking and join that to #bug. I would do:
select t1.id, t1.name, t2.name as `blockingName`
from `#bug` t1
left join (select * from `#blocking` where name = "qa bug") t2
on t1.id = t2.id
select *
from #bug t1
left join #blocking t2 on t1.id = t2.id and t2.name = 'qa bug'
make sure the inner query only returns one row.
You may have to add a top 1 on it if it returns more than one.
select
t1.id, t1.name,
(select b.name from #blocking b where b.id=t1.id and b.name='qa bug')
from #bug t1
Here's a demo: http://sqlfiddle.com/#!2/414e6/1
select
bug.id,
bug.name,
blocking.name as blockingType
from
bug
left outer join blocking on
bug.id = blocking.id AND
blocking.name = 'qa bug'
order by
bug.id
By adding the "blocking.name" clause under the left outer join, rather than to the where, you indicate that it should also be consider "outer", or optional. When part of the where clause, it is considered required (which is why the null values were being filtered out).
BTW - sqlfiddle.com is my site.
I've got two tables:
User (id, name, etc)
UserRight (user_id, right_id)
I want to find the users who have rights 1, 2 and 3, but no users who only have one or two of these. Also, the number of rights will vary, so searches for (1,2,3) and (1,2,3,4,5,6,7) should work with much the same query.
Essentially:
SELECT *
FROM User
WHERE (
SELECT right_id
FROM tblUserRight
WHERE user_id = id
ORDER BY user_id ASC
) = (1,2,3)
Is this possible in MySQL?
SELECT u.id, u.name ...
FROM User u
JOIN UserRight r on u.id = r.user_id
WHERE right_id IN (1,2,3)
GROUP BY u.id, u.name ...
HAVING COUNT DISTINCT(right_id) = 3
You can also do this using PIVOT, especially if you want a visual representation. I did this on SQL Server - you may be able to translate it.
Declare #User Table (id Int, name Varchar (10))
Declare #UserRight Table (user_id Int, right_id Int)
Insert Into #User Values (1, 'Adam')
Insert Into #User Values (2, 'Bono')
Insert Into #User Values (3, 'Cher')
Insert Into #UserRight Values (1, 1)
Insert Into #UserRight Values (1, 2)
Insert Into #UserRight Values (1, 3)
--Insert Into #UserRight Values (2, 1)
Insert Into #UserRight Values (2, 2)
Insert Into #UserRight Values (2, 3)
Insert Into #UserRight Values (3, 1)
Insert Into #UserRight Values (3, 2)
--Insert Into #UserRight Values (3, 3)
SELECT *
FROM #User U
INNER JOIN #UserRight UR
ON U.id = UR.User_Id
PIVOT
(
SUM (User_Id)
FOR Right_Id IN ([1], [2], [3])
) as xx
WHERE 1=1
SELECT *
FROM #User U
INNER JOIN #UserRight UR
ON U.id = UR.User_Id
PIVOT
(
SUM (User_Id)
FOR Right_Id IN ([1], [2], [3])
) as xx
WHERE 1=1
AND [1] IS NOT NULL
AND [2] IS NOT NULL
AND [3] IS NOT NULL
In correspondance with the errors in my answer pointed out, here a solution with count and a subquery:
SELECT *
FROM User
WHERE 3 = (
SELECT Count(user_id)
FROM tblUserRight
WHERE right_id IN (1,2,3)
AND user_id = User.id
)
An optimizer may of course change this to Martin Smith's solution (i.e. by using a group by).