How to use SQL coalesce with a null whole fetched row - sql

The case I am trying to solve is this: for every row in a table another row from a second table might exist, so I need all data from the row of the first table and the data from the row of the second table if present.
I know I can use data structures as host variables to gather all data from a row in a table. So, my select is this:
select
t1.*
,t2.*
into
:dst1
,:dst2
from table1 t1
left join table2 t2 on t2.key=t1.key
;
where dst1 and dst2 are data structures respectively like table1 and table2 records' format. Pretty simple.
Now, the point is how to catch null result when a row for that key doesn't exist in the second table. In that case I would like to have the corresponding data structure initialized, but coalesce works on one field at a time and I haven't been able to find another solution.
Is there a way to obtain this result?
Any help would be appreciated!
Thanks

One way to deal with this is to use indicator variables. It looks like this:
dcl-ds hs Qualified;
field1 ...
field2 ...
endds;
dcl-s hsind Int(5) Dim(2);
exec sql
select *
into :hs:hsind
from table
fetch first row only;
Note, there is no comma (,) between :hs and :hsind as this is part of the same variable assignment. :hsind is an indicator variable, and in this case is an array of Int(5) with the same number of elements as the host data structure :hs has fields. The indicator variable will contain a 0 if the value in the associated field in :hs is good, or -1 if it is null. So in our example above: If hs.field1 is good, and hs.field2 is null, then hsind(1) = 0, and hsind(1) = -1. Other values mean other things like data mapping error (-2), or string truncation (positive number with original length of string).
So in your example, use something like this:
select
t1.*
,t2.*
into
:dst1:dst1ind
,:dst2:dst2ind
from table1 t1
left join table2 t2 on t2.key=t1.key
;
Where dst1ind is an array if Int(5) with the same number of elements as dst1 has subfields, similarly for dst2ind. Then after your selection, just check dst2ind(1) >= 0, and you have a good select. Notice that you will need to make sure that select into only returns a single row, or you will get errors about that.

Related

Query results to temp, then move to mast table

I don't know what I am doing. Extra new at this. Below I am trying to make corrections to the data on a column, with out losing any of the data, just overriding it. In this Column, some of the cells have characters spaces (spacebar) in them so it dose not show up as "NULL".
In my fist attempt, I can see the query data and it looks good, 100% correct. But don't know how to put that data into the table I got it from. So I need to replace the data in column 'Speedlink_IP' with my Queried results.
Thanks every one in advance!
1st Attempt -
SELECT NULLIF(LTRIM(RTRIM(Speedlink_IP)), '')
As Speedlink_IP
FROM Master_IP_Data
INSERT INTO TEMP1 (col1)
2nd attempt -
CREATE TABLE TEMP1 (
col1 varchar (50) NULL
);
SELECT NULLIF(LTRIM(RTRIM(Speedlink_IP)), '')
As Speedlink_IP
FROM Master_IP_Data
INSERT INTO TEMP1 (col1)
INSERT INTO dbo.Master_IP_Data (Speedlink_IP)
SELECT col1
FROM TEMP1
;
DROP Table TEMP1
You seem to be looking for a simple UPDATE statement.
UPDATE Master_IP_Data
SET Speedlink_IP = NULL
WHERE LTRIM(RTRIM(Speedlink_IP)) = ''
This query will turn to NULL values of Speedlink_IP that contains only spaces. You don't need to use a temporary table for this.

How to insert generated id into a results table

I have the following query
SELECT q.pol_id
FROM quot q
,fgn_clm_hist fch
WHERE q.quot_id = fch.quot_id
UNION
SELECT q.pol_id
FROM tdb2wccu.quot q
WHERE q.nr_prr_ls_yr_cov IS NOT NULL
For every row in that result set, I want to create a new row in another table (call it table1) and update pol_id in the quot table (from the above result set) with the generated primary key from the inserted row in table1.
table1 has two columns. id and timestamp.
I'm using db2 10.1.
I've tried numerous things and have been unsuccessful for quite a while. Thanks!
Simple solution: create a new table for the result set of your query, which has an identity column in it. Then, after running your query, update the pol_id field with the newly generated ID in your result table.
Alteratively, you can do it more manually by using the the ROW_NUMBER() OLAP function, which I often found convenient for creating IDs. For this it is convenient to use a stored procedure which does the following:
get the maximum old id from Table1 and write it into a variable old_max_id.
after generating the result set, write the row-numbers into the table1, maybe by something like
INSERT INTO TABLE1
SELECT ROW_NUMBER() OVER (PARTITION BY <primary-key> ORDER BY <whatever-you-want>)
+ OLD_MAX_ID
, CURRENT TIMESTAMP
FROM (<here comes your SQL query>)
Either write the result set into a table or return a cursor to it. Here you should either use the same ROW_NUMBER statement as above or directly use the ID from Table1.

cannot insert value NULL into column error shows wrong column name

I've added a new column(NewValue) to my table which holds an int and allows nulls. Now I want to update the column but my insert statement only attempts to update the first column in the table not the one I specified.
I basically start with a temp table that I put my initial data into and it has two columns like this:
create table #tempTable
(
OldValue int,
NewValue int
)
I then do an insert into that table and based on the information NewValue can be null.
Example data in #tempTable:
OldValue NewValue
-------- --------
34556 8765432
34557 7654321
34558 null
Once that's complete I planned to insert NewValue into the primary table like so:
insert into myPrimaryTable(NewValue)
select tt.NewValue from #tempTable tt
left join myPrimaryTable mpt on mpt.Id = tt.OldValue
where tt.NewValue is not null
I only want the NewValue to insert into rows in myPrimaryTable where the Id matches the OldValue. However when I try to execute this code I get the following error:
Cannot insert the value NULL into column 'myCode', table 'myPrimaryTable'; column does not allow nulls. INSERT fails.
But I'm not trying to insert into 'myCode', I specified 'NewValue' as the column but it doesn't seem to see it. I've checked NewValue and it is set to allow int and is set to allow null and it does exist on the right table in the right database. The column 'myCode' is actually the second column in the table. Could someone please point me in the right direction with this error?
Thanks in advance.
INSERT always creates new rows, it never modifies existing rows. If you skip specifying a value for a column in an INSERT and that column has no DEFAULT bound to it and is not identity, that column will be NULL in the new row--thus your error. I believe you might be looking for an UPDATE instead of an INSERT.
Here's a potential query that might work for you:
UPDATE mpt
SET
mpt.NewValue = tt.NewValue
FROM
myPrimaryTable mpt
INNER JOIN #tempTable tt
ON mpt.Id = tt.OldValue -- really?
WHERE
tt.NewValue IS NOT NULL;
Note that I changed it to an INNER JOIN. A LEFT JOIN is clearly incorrect since you are filtering #tempTable for only rows with values, and don't want to update mpt where there is no match to tt--so LEFT JOIN expresses the wrong logical join type.
I put "really?" as a comment on the ON clause since I was wondering if OldValue is really an Id. It probably is--you know your table best. It just raised a mild red flag in my mind to see an Id column being compared to a column that does not have Id in its name (so if it is correct, I would suggest OldId as a better column choice than OldValue).
Also, I recommend that you never name a column just Id again--column names should be the same in every table in the database. Also, when it comes join time you will be more likely to make mistakes when your columns from different tables can coincide. It is much better to follow the format of SomethingId in the Something table, instead of just Id. Correspondingly, the suggested old column name would be OldSomethingId.

Look Up Table in SQL

I basically have a table A with 30 million records and I want to add a column entitled "TYPE" to the table. I have a look up table B that maps a code to a color. I want to iterate through table A and compare the code in TABLE A to the code in TABLE B and then add the color to the TYPE column in table A. Is this possible? What would be the best approach to this problem? The codes in table B don't match perfectly with the actual codes in table A.
hard to say without seeing the schema or knowing the DBMS but, if it's always a the first 2 digits of the code used to look up the color, why not
UPDATE table_a SET type = SUBSTR(code, 2)
and do a JOIN normally
you could do a join like
JOIN table_b ON table_b.id = SUBSTR(table_a.code,2)
but that would hardly be performant.
Give or take issues with size of transaction, isn't it a simple ALTER TABLE to add the column and an UPDATE to fix it?
ALTER TABLE TableA ADD (Type VARCHAR(10));
UPDATE TableA
SET Type = ((SELECT Colour FROM TableB WHERE TableA.Type = TableB.Type));
The only tricky bit might be the double parentheses; they're needed in some DBMS and may not be needed in others (single parentheses may be sufficient). You may also be able to use a UPDATE with a JOIN; the syntax isn't entirely standard there, either.
Note that this mapping relies on a change of NULL to NULL being a no-op. If you have values in some rows but not all of those rows match an entry in TableB, then you need to be careful with an full-table UPDATE like that. It will set the Type for any rows in TableA for which there is no match in TableB to NULL. If that wasn't what you needed, you'd either use an UPDATE with JOIN or you'd write:
UPDATE TableA
SET Type = ((SELECT Colour FROM TableB WHERE TableA.Type = TableB.Type));
WHERE Type IN (SELECT Colour FROM TableB WHERE TableA.Type = TableB.Type);
In the current case, where the column is newly added and therefore contains NULL anyway, there is no harm done omitting the WHERE clause on the UPDATE itself.
You can do the update just by looking at the first characters of the code in table B, as in the following statement:
update table_a
set table_a.type = table_b.type
from table_b
where table_b.code = substr(table_a.code, 1, length(table_b.code))
This may be a bit slow, since you cannot use any indexes to speed it up. However, if table_b is small, then the performance may be acceptable.

SQL query select from table and group on other column

I'm phrasing the question title poorly as I'm not sure what to call what I'm trying to do but it really should be simple.
I've a link / join table with two ID columns. I want to run a check before saving new rows to the table.
The user can save attributes through a webpage but I need to check that the same combination doesn't exist before saving it. With one record it's easy as obviously you just check if that attributeId is already in the table, if it is don't allow them to save it again.
However, if the user chooses a combination of that attribute and another one then they should be allowed to save it.
Here's an image of what I mean:
So if a user now tried to save an attribute with ID of 1 it will stop them, but I need it to also stop them if they tried ID's of 1, 10 so long as both 1 and 10 had the same productAttributeId.
I'm confusing this in my explanation but I'm hoping the image will clarify what I need to do.
This should be simple so I presume I'm missing something.
If I understand the question properly, you want to prevent the combination of AttributeId and ProductAttributeId from being reused. If that's the case, simply make them a combined primary key, which is by nature UNIQUE.
If that's not feasible, create a stored procedure that runs a query against the join for instances of the AttributeId. If the query returns 0 instances, insert the row.
Here's some light code to present the idea (may need to be modified to work with your database):
SELECT COUNT(1) FROM MyJoinTable WHERE AttributeId = #RequestedID
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO MyJoinTable ...
END
You can control your inserts via a stored procedure. My understanding is that
users can select a combination of Attributes, such as
just 1
1 and 10 together
1,4,5,10 (4 attributes)
These need to enter the table as a single "batch" against a (new?) productAttributeId
So if (1,10) was chosen, this needs to be blocked because 1-2 and 10-2 already exist.
What I suggest
The stored procedure should take the attributes as a single list, e.g. '1,2,3' (comma separated, no spaces, just integers)
You can then use a string splitting UDF or an inline XML trick (as shown below) to break it into rows of a derived table.
Test table
create table attrib (attributeid int, productattributeid int)
insert attrib select 1,1
insert attrib select 1,2
insert attrib select 10,2
Here I use a variable, but you can incorporate as a SP input param
declare #t nvarchar(max) set #t = '1,2,10'
select top(1)
t.productattributeid,
count(t.productattributeid) count_attrib,
count(*) over () count_input
from (select convert(xml,'<a>' + replace(#t,',','</a><a>') + '</a>') x) x
cross apply x.x.nodes('a') n(c)
cross apply (select n.c.value('.','int')) a(attributeid)
left join attrib t on t.attributeid = a.attributeid
group by t.productattributeid
order by countrows desc
Output
productattributeid count_attrib count_input
2 2 3
The 1st column gives you the productattributeid that has the most matches
The 2nd column gives you how many attributes were matched using the same productattributeid
The 3rd column is how many attributes exist in the input
If you compare the last 2 columns and the counts
match - you can use the productattributeid to attach to the product which has all these attributes
don't match - then you need to do an insert to create a new combination