Update duplicate columns table with join - sql

I am having a problem for Oracle update SQL with join. I have a table A which has 5 columns (C1, C2, C3, C4, C5); table B which has 3 columns (D1, D2, D3). For table A, I only use 2 columns (C2, C3) to join table B (D1, D2), and update table A column C2 with table B column D3.
For table A, it will be lots of duplicates for both C2 and C3; but for condition C4 as 10, there will be no duplicates which is what I want to update.
For example, table A has records as:
1,100,1500,10,'ORG'
1,200,2000,10,'ORG'
1,300,2500,10,'ORG'
2,1000,500,20,'PERSON'
2,1000,200,20,'PERSON'
2,2000,200,20,'PERSON'
You can see that for C4 as 10, there is no duplicate for C2 and C3. But for C4 as 20, there will be duplicates for C2 and C3.
For table B, it will be like
100,1500,80
200,2000,100
300,3000,200
There is no duplicates for table B, and will join B with A as A.C2 = B.D1 and A.C3 = B.D2 to update A.C2 to B.D3.
I only have to update C4=10 records to join with table B based on B.D1 and B.D2.
I have SQL as below, but failed as
ORA-01779: cannot modify a column
which maps to a non key-preserved
table
Can anyone tell me what is wrong with my SQL?
UPDATE (
SELECT A.C2 OID, B.D3 TID FROM A, B
WHERE A.C2 = B.D1 AND A.C3 = B.D2 AND A.C4=10 AND B.D3 <> ' ' )
SET OID = TID
Thanks a lot!

Can't you use an update statement like...
UPDATE A
set c2 = (select D3
from B
where B.D2 <> ' ' and
A.C2 = b.D1 and A.C3=B.D2)
)
where A.C4=10
Also, your question and the problem description itself looks like a puzzle thtis very hard to understand.
Can you post some data and create table statements so that it's easy to reproduce your case ?(from next time maybe....)

First, it sounds like you are trying to update TableA. Second, the official ANSI specification for the Update statement does not provide for a join directly in the statement (you can have joins only through subqueries). One reason is that it would create ambiguities when duplicate rows were involved. So, you might try something like:
Update A
Set OID = (
Select B.D3
From B
Where B.D1 = A.C2
And B.D2 = A.C3
And B.D3 Is Not Null
)
Where A.C4 = 10
And Exists (
Select 1
From B
Where B.D1 = A.C2
And B.D2 = A.C3
And B.D3 Is Not Null
)

UPDATE A
SET C2 = (
SELECT B.D3
FROM B
WHERE A.C2 = B.D1 AND A.C3 = B.D2)
WHERE A.C4=10
AND EXISTS (
SELECT *
FROM B2
WHERE A.C2 = B2.D1 AND A.C3 = B2.D2)
No need to alias for update statements. Also you have the condition on B.D2 <> ' ' and also A.C3 = B.D2, so it is just as easy to just filter on A.C3 <> ' ', since A is the target table. If A.C3 is never blank, then the condition is not even required.

Related

Update values from one table in another

I have tables A,B and C. I need to select certain values from all rows in Table A and then for every row in A, I need to update values in Table B and Table C.
Pseudo Code would look like this:
SELECT A1, A2, A3, A4 FROM Table A
UPDATE Table B SET B2=A2, B3=A3, B4=A4 WHERE B1 = A1;
UPDATE Table C SET C2=A2, C3=A3, C4=A4 WHERE C1 = A1;
How can I achieve this?
In Oracle, you would use two update statements:
update b
set (b2, b3, b4) = (select a2, a3, a4 from a where a.a1 = b.b1);
update c
set (c2, c3, c4) = (select a2, a3, a4 from a where a.a1 = c.b1);
You need two update statements. You could use an inline view:
update (
select a.a2, a.a3, a.a4 , b.b2, b.b3, b.b4
from a
inner join b on b.b1 = a.a1
) u
set u.b2 = u.a2, u.b3 = u.a3, u.b4 = u.a4
The upside is that this only updates matching rows - while, using the correlated subquery technique, you need to repeat the subquery in the where clause.
Another neat syntax that does what you want in Oracle is merge:
merge into b
using a on (a.a1 = b.a1)
when matched then update set b.b2 = a.a2, b.b3 = a.a3, b.b4 = a.a4;

SQL Server 2008 EXCEPT statement

Here is my example script:
SELECT c2, c3, c4 FROM Table1
EXCEPT
SELECT c2, c3, c4 FROM Table2
I'm successfully returning unique records from the left table that do not also exist in the right table. Both tables have identical schemas and for the most part identical data. The problem is that the unique id (let's call it column c1) does not match, so I need to exclude it in the EXCEPT query above. How can I return the same set of records, but with the unique IDs included?
I was thinking of using temporary tables, cursors and long WHERE statements inside the cursor, but that doesn't seem like a very elegant solution. is there another way to accomplish this seemingly simple task?
Can you take your supplied query, and simply inner join it with table 1 to get your 'c1' column?
SELECT T1.* FROM Table1 T1 INNER JOIN(
SELECT c2, c3, c4 FROM Table1
EXCEPT
SELECT c2, c3, c4 FROM Table2
) a on a.c2=T1.c2 and a.c3=T1.c3 and a.c4=T1.c4
Try this
SELECT A.c1, A.c2, A.c3, A.c4
FROM Table1 A
LEFT OUTER JOIN Table2 B ON A.c2 = B.C2 AND A.c3 = B.C3 AND A.c4 = B.C4
WHERE B.c1 IS NULL;
You probably can accomplish it using "NOT EXISTS" rather than "EXCEPT" since with "NOT EXISTS" you can specify conditions. Here's a thread that points this out: EXCEPT vs NOT EXISTS.
This is kind of ugly and, on large tables lacking "useful" indexes, might perform very poorly, but it will do the work:
SELECT t1.c1, t1.c2, t1.c3, t1.c4
from Table1 t1
inner join (-- Unique tuples
SELECT c2, c3, c4 FROM Table1
EXCEPT
SELECT c2, c3, c4 FROM Table2
) xx
on xx.c2 = t1.c2
and xx.c3 = t1.c3
and xx.c5 = t1.c4

How to create indexes for query for better performance in sybase ase 15.4?

I have 3 large tables: tab1, tab2, tab3, tab4. I have a sql query like:
select * from tab1 a, tab2 b, tab3 c, tab4 d
where
a.c1 = b.c1
and b.c2 = c.c2
and c.c3 = d.c3
and a.c4 = c.c4
and a.c3 = #var3
and a.c4 = #var4
and b.date >= #d1 and b.date <=#d2
and b.c5 =#var5
group by d.c6, a.c4, c.c7
order by d.c6, a.c4, c.c7
There are indexes created as: for each column in the where condition, there is a index created. For example, there are 3 columns in tab1 used in above query: c1, c3, c4. there are 3 indexes created:
index on c1, index on c3, index onc4.
Same for all other tables.
Questions:
1. How to create a index for those columns appear in where condition? single colum index or combined index? For example, for tab1, one index include 3 columns c1, c3, c4 or 3 index for each single column?
2. If index also should be create for columns in group by?
3. If the order in where condition is important? For example, following 3 has any difference on performance?
where
a.c1 = b.c1
and b.c2 = c.c2
and c.c3 = d.c3
and a.c4 = c.c4 ----duplicate join???
and a.c3 = #var3
and a.c4 = #var4
and b.date >= #d1 and b.date <=#d2
and b.c5 =#var5
Or
where
a.c3 = #var3
and a.c4 = #var4
and b.date >= #d1 and b.date <=#d2
and b.c5 =#var5
and a.c1 = b.c1
and b.c2 = c.c2
and c.c3 = d.c3
and a.c4 = c.c4 --duplicate join???
Or
where
a.c3 = #var3
and a.c4 = #var4
and b.date >= #d1 and b.date <=#d2
and b.c5 =#var5
and a.c1 = b.c1
and b.c2 = c.c2
and c.c3 = d.c3
How big is the table? What kind of locking scheme tables are following?
Generally, clustered indexes work better in case of ranged query but if tables are following DOL scheme then it's no good.
It's really hard to tell which indexes should be created without looking at the table but I would initially build them like
Tab1: 2 different non-clustered index on C3 and C4
Tab2: clustered index on date, non clustered index on C5
Tab3: Unique clustered index in C2, C3 and C4
I also would break the query in smaller pieces:
one for taking out all the rows (put them in #temp1)
Create clustered index on C6, C4, C7 on #temp1
write the final query with group by clause where only #temp1 will participate in group by clause. You can skip order by clause since we already have clustered on these columns

SQL assign value for join purpose to null field without update

With this sample data...
c1 c2 c3
10 a 10a
11 a **NULL**
12 a **NULL**
13 b 13b
13 b **NULL**
etc..
I want to assign c3 value to rows where it is NULL and c2 value is the same, but do not wish to actually update that value in table, only use it to join to another table while select runs.
I am able to do it using inner select and join, but I want to save as much processing power as possible because amount of data is huge and thought that some use of ISNULL or COALESCE should be able to do it, but I don't have enough experience to figure it on my own yet, or even say if it is possible. What do you think?
select
a.c1,
a.c2,
coalesce( a.c3, b.c3) as c3
from table1 a
left join table1 b
on a.c2 = b.c2
an a.c3 is null
and b.c3 is not null
this will work with the data that you have however if there is more than one row that is not null for c3 for a given c2 it will cause problems
If your DB supports max() over you can also do this
SELECT c1,
c2,
coalesce(c3, MAX(C3) over (partition by c2)) c3
from table1
demo
A sample pseudo code which i often use in my Query.
DECLARE #USER_ID AS VARCHAR(256)
SELECT #USER_ID = 'abc'
select *
from CC
INNER JOIN adm_co_users CR_US WITH(NOLOCK) ON ISNULL(CR_US.original_userID,'') =
(CASE ISNULL(CC.Createdby,'') WHEN '' THEN #USER_ID
ELSE CC.Createdby END )

Comma Separated list of rows of a column with group by on other columns

Below is the structure of table I have: -
Table T1
C1 C2 C3
----------
X P A
X P B
Y Q C
Y Q D
Desired output: -
C1 C2 C3
------------
X P A,B
Y Q C,D
Note: - I know i can do the same with For XML('') with group by on C1 and C2, but the main problem in my case is that the table T1 here must be a physical table object (either permanent or temp or table var or CTE) in DB. But in my case it's a derived table and when i am using the below query it's saying invalid object.
In my case it's not good to replace the derived table with temp# tables or fixed tables or even with CTE or table variable because it will take a great effort.
SELECT
b.C1, b.C2, Stuff((',' + a.C3 from t1 a where a.c1 = b.c1 for XML PATH('')),1,1,'') FROM
T1 b group by b.c1,b.c2
I did not have T1 as fixed table. Please consider it as derived table only.
I need the solution with existing derived table.
Please help.
Below is the query with derived table: -
Please consider this only as a demo query. It's not as simple as given below and a lot of calculations have done to get the derived tables and 4 levels of derived tables have been used.
SELECT C1, C2, Stuff((',' + a.C3 from A B where a.c1 = b.c1 for XML PATH('')),1,1,'')
FROM
(
SELECT C1, C2, C3 FROM T1 WHERE C1 IS NOT NULL--and a lot of calculation also
)A
Please mind that T1 is not just below one step, in my case T1 the actual physical table is 4 level downs by derived tables.
If you can post the query the produces derived table, we can help you work it out, but as of the moment try substituting table1 with the derived query.
;WITH Table1
AS
(
SELECT C1, C2, C3 FROM T1 WHERE C1 IS NOT NULL--and a lot of calculation also
)
SELECT
C1,C2,
STUFF(
(SELECT ',' + C3
FROM Table1
WHERE C1 = a.C1 AND C2 = a.C2
FOR XML PATH (''))
, 1, 1, '') AS NamesList
FROM Table1 AS a
GROUP BY C1,C2
SQLFiddle Demo