unable to get a stable set of rows error - sql

I am trying to perform a merge into a table (let's call it table1) from a table2. In the USING condition I need a third table (table3). This third table contains some IDs that I need in table1. A simplified version of my merge looks like:
MERGE INTO table1 a
USING (
SELECT ID, address
FROM table3 b
Where address IN
(
SELECT address
FROM table3
WHERE address IS NOT NULL
AND ID> 0
GROUP BY address
HAVING COUNT(*) = 1
)
) c
ON (a.address = c.address)
WHEN MATCHED THEN
UPDATE SET a.ID = c.ID
WHERE a.ID = 0
I know that the error I get is usually caused by the query in the USING clause, but theoretically this problem should be eliminated by the count(*)=1 condition.
I have duplicates in table2, but they should all get an ID from table3 or ID 0 if the address is duplicated in table3.
IDs are unique for an address, so they should be distinct.
P.S. This merge is performed automatically by a script that , so I can modify the query to add more conditions/restrictions, but I cannot change the structure [meaning I have to use these 3 tables as they are].
I hope this makes sense.
Any ideas why this still does not work for me?

Try this:
MERGE INTO table1 a
USING (
SELECT max(ID), address
FROM table3 b
WHERE address IS NOT NULL AND ID > 0
GROUP BY address
HAVING COUNT(*) = 1
) c
ON (a.address = c.address)
WHEN MATCHED THEN
UPDATE SET a.ID = c.ID
WHERE a.ID = 0;
you have where condition in inner query but not in outer query. If you want your original query please try:
MERGE INTO table1 a
USING (
SELECT ID, address
FROM table3 b
AND address IN
(
SELECT address
FROM table3
WHERE address IS NOT NULL
AND ID> 0
GROUP BY address
HAVING COUNT(*) = 1
)
WHERE address IS NOT NULL
AND ID> 0
) c
ON (a.address = c.address)
WHEN MATCHED THEN
UPDATE SET a.ID = c.ID
WHERE a.ID = 0

The issue is more than likely due to the duplicate rows from table2. Here's a simple test case demonstrating the issue:
Setup:
CREATE TABLE t1 (ID INTEGER PRIMARY KEY,
val VARCHAR2(1));
CREATE TABLE t2 (ID INTEGER,
val VARCHAR2(1));
INSERT INTO t1 (ID, val) VALUES (1, 'A');
INSERT INTO t2 (ID, val) VALUES (1, 'B');
INSERT INTO t2 (ID, val) VALUES (1, 'B');
COMMIT;
Merge that will error:
MERGE INTO t1 USING t2
ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET t1.val = t2.val;
ORA-30926: unable to get a stable set of rows in the source tables
Merge that will succeed:
MERGE INTO t1 USING (SELECT DISTINCT id, val FROM t2) t2
ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET t1.val = t2.val;
N.B. The second merge will still fail if you have different values returned for val for the same id; that means you will have more than one row returned for a given id, and Oracle won't know which one to use to update the target table with.
In order to make sure your merge statement will work, you will need to ensure that you will return at most 1 row per address in the source subquery.

Related

Updating a key on table from another table in Oracle

I am trying to update a key on a table (t1) when the key value is (abc) by getting the value from table (t2).
It is working as expected when I am limiting it to a specific person
update table_a t1
set t1.u_key = (select t2.u_key
from table_b t2
where t2.name_f=t1.name_f
and t2.name_l=t1.name_l
and rownum<=1
and t2='NEVADA')
where t1.u_key = 'abc'
and e.name_f='Lori'
and e.name_l='U'
;
I initially tried without rownum and it said too many rows returned.
To run on all the data with t1.u_key='abc' and took out the specific name, I tried this which has been running until time out.
update table_a t1
set t1.u_key = (select t2.u_key
from table_b t2
where t2.name_f=t1.name_f
and t2.name_l=t1.name_l
and rownum<=1
and t2='NEVADA')
where t1.u_key = 'abc'
;
Can you please look at it and suggest what am I missing.
You should first take a look what is returned when you run the inner SELECT statement alone:
SELECT t2.u_key FROM table_b t2
WHERE t2.name_f IN (SELECT name_f FROM table_a WHERE u_key = 'abc')
AND t2.name_l IN (SELECT name_l FROM table_a WHERE u_key = 'abc')
AND t2='NEVADA'
Examine the results and you will see that there are more than one row returned.
If there should be only matching row per key, you would need to add the key to the inner SELECT as well but I can't tell you how it should look like without additional table descriptions and possibly some sample entries from table_a and table_b.
Use this:
update (
SELECT t2.u_key t2key,
t1.ukey t1key
FROM table_b t2,
table_a t1
where t2.name_f=t1.name_f
and t2.name_l=t1.name_l
and t2='NEVADA'
and rownum<=1 )
SET t1key = t2key
where t1key = 'abc';
merge into table_a t1
using(
select name_f, name_l, max(u_key) as new_key
from table_b t2
where t2='NEVADA'
group by name_f, name_l
) t2
on (t1.name_f=t2.name_f and t1.name_l=t2.name_l and t1.u_key='abc')
when matched then
update set t1.u_key=t2.new_key

SQL statement to conditionally select related records

I have a table with fields id (primary key) and fid. I want to get the record where id matches a particular value, as well as all related records that have its same fid value.
I can do this:
SELECT * FROM mytable
WHERE fid = (SELECT TOP 1 fid FROM mytable WHERE id = 'somevalue')
But I don't want the related records if the fid is a particular value (in my case an empty guid value).
Is there a way to do this in a single SQL statement? I am using SQL Server 2008 R2.
UPDATE:
Looking at the answers so far I think I may not have asked my question clearly. id and fid will never be equal. LEFT JOIN may be what I need, but I'm a bit SQL ignorant. What I'm hoping for is the following two queries as a single statement:
SELECT * FROM mytable WHERE id = 'somevalue'
SELECT * FROM mytable WHERE fid =
(SELECT TOP 1 fid FROM mytable
WHERE id = 'somevalue' AND fid != '00000000-0000-0000-0000-000000000000')
Based on your revision, the problem seems to be "select all rows where id has a certain value and all other rows with the id matches "somevalue" and the fid is not null.
The following captures this logic:
SELECT t.*
FROM mytable t left outer join
(SELECT TOP 1 fid
FROM mytable
WHERE id = 'somevalue' AND fid <> '00000000-0000-0000-0000-000000000000'
) t1
on t.fid = t1.fid
WHERE id = 'somevalue' or t1.fid is not null;
Because id is a primary key, the t1 subquery will return 0 or 1 rows. When it returns 0 rows, you will only get the original row matching 'somevalue'.
I'm not certain I understand your question, but I'll take a stab at it. What I think you're asking is if you can select all records from one table where either the id or fid fields equal a particular value, but you don't want the related fields if the particular value you're searching on equals an empty guid value. If so, here's how you can do it:
SELECT
*
FROM
mytable t1
LEFT JOIN
mytable t2 ON (t1.id = t2.fid) AND (t2.fid IS NOT NULL);
Is this what you were looking for?
I think this is what you are trying to do:
SELECT *
FROM mytable a
JOIN mytable b ON a.id = b.fid
WHERE a.id = 'somevalue';
This should return all records in a (joined with all records in b where a.id = b.fid) then filtered to show only records that have a.id = 'somevalue';
You could just add another clause to your sql statement like this:
SELECT * From mutable
WHERE fid = (SELECT TOP 1 fid FROM mytable WHERE id = 'somevalue'
AND fid != '00000000-0000-0000-0000-000000000000')
If you want more than one row, try a join as suggested by #zigdawgydawg.
Maybe this is what you are after:
select * from mytable
where id = 'somevalue'
or id = (select fid from mytable where id = 'somevalue')
Almost like zigdawgydawg's contribution, but slightly different:
SELECT * FROM mytable WHERE fid IN
(SELECT fid FROM mytable WHERE id = 'somevalue' )
AND NOT guid is null;

tsql - updating a table from a select statement

I have two tables. TableA and TableB.
TableA holds a varbinary(max) column - named [BinaryA]
TableB holds a column (named "Volume", type "Long") that contains each varbinary volume.
in order to select all volumes I query
SELECT ID, MyVolume = DATALENGTH([Binary])
FROM [VAULT].[TABLEA]
than, I want to update tableB with its volume.
I then write
update [TableB]
set [VOLUME] = ( SELECT MyVolume = DATALENGTH([Binary])
FROM [VAULT].[TABLEA] ab
WHERE id = ab.[Id])
I receive than
Cannot insert the value NULL into column 'Volume', table 'MySchema.Asset';
column does not allow nulls. UPDATE fails.
Though I dont receive any NULL when I run
SELECT ID, MyVolume = DATALENGTH([Binary])
FROM [VAULT].[TABLEA]
Try with this query:
UPDATE TableB
SET TableB.[VOLUME] = DATALENGTH([T2.Binary])
FROM TableB
INNER JOIN [VAULT].[TABLEA] T2 ON TableB.TAL_ID = T2.TAL_ID
Assuming that TableB and [VAULT].[TABLEA] are related by ID field.
UPDATE tableB
SET [volume] = (SELECT RetrieveAccountNumber.AccountNumber
FROM RetrieveAccountNumber
WHERE tableB.leadid =RetrieveAccountNumber.LeadID)
WHERE Sales_Import.leadid = (SELECT RetrieveAccountNumber.LeadID
FROM RetrieveAccountNumber
WHERE tableB.leadid = RetrieveAccountNumber.LeadID)

SQL Server update statement using a join on multiple columns

Having some trouble here.
Table 1:
CID, Name, HID
(001-233, Test1, 12345)
Table 2:
CID, Name, HID, Primary
(001-233, Test1, 12345, '')
Want to update Table2 where a join exists with Table1 with a constant value called 'Y'
So statement is as follows:
UPDATE T2 SET T2.Primary = 'Y'
FROM T2
INNER JOIN T1
ON (T1.CID = T2.CID
AND T1.HID = T2.HID)
This statement just ends up updating all the rows, it's like it only does a join on one id and not the other? I finally gave up and did a WHERE IN subquery with a single "id" by concatenating the two ide fields CID+HID. But I want to understand why this didnt work using the proper join.
Table1 is a CTE..
update t2
set (t2.primary) = (select 'Y' from t1 where T1.CID = T2.CID AND T1.HID = T2.HID)

How do I compare 2 rows from the same table (SQL Server)?

I need to create a background job that processes a table looking for rows matching on a particular id with different statuses. It will store the row data in a string to compare the data against a row with a matching id.
I know the syntax to get the row data, but I have never tried comparing 2 rows from the same table before. How is it done? Would I need to use variables to store the data from each? Or some other way?
(Using SQL Server 2008)
You can join a table to itself as many times as you require, it is called a self join.
An alias is assigned to each instance of the table (as in the example below) to differentiate one from another.
SELECT a.SelfJoinTableID
FROM dbo.SelfJoinTable a
INNER JOIN dbo.SelfJoinTable b
ON a.SelfJoinTableID = b.SelfJoinTableID
INNER JOIN dbo.SelfJoinTable c
ON a.SelfJoinTableID = c.SelfJoinTableID
WHERE a.Status = 'Status to filter a'
AND b.Status = 'Status to filter b'
AND c.Status = 'Status to filter c'
OK, after 2 years it's finally time to correct the syntax:
SELECT t1.value, t2.value
FROM MyTable t1
JOIN MyTable t2
ON t1.id = t2.id
WHERE t1.id = #id
AND t1.status = #status1
AND t2.status = #status2
Some people find the following alternative syntax easier to see what is going on:
select t1.value,t2.value
from MyTable t1
inner join MyTable t2 on
t1.id = t2.id
where t1.id = #id
SELECT COUNT(*) FROM (SELECT * FROM tbl WHERE id=1 UNION SELECT * FROM tbl WHERE id=2) a
If you got two rows, they different, if one - the same.
SELECT * FROM A AS b INNER JOIN A AS c ON b.a = c.a
WHERE b.a = 'some column value'
I had a situation where I needed to compare each row of a table with the next row to it, (next here is relative to my problem specification) in the example next row is specified using the order by clause inside the row_number() function.
so I wrote this:
DECLARE #T TABLE (col1 nvarchar(50));
insert into #T VALUES ('A'),('B'),('C'),('D'),('E')
select I1.col1 Instance_One_Col, I2.col1 Instance_Two_Col from (
select col1,row_number() over (order by col1) as row_num
FROM #T
) AS I1
left join (
select col1,row_number() over (order by col1) as row_num
FROM #T
) AS I2 on I1.row_num = I2.row_num - 1
after that I can compare each row to the next one as I need