SQL server update table with missing values - sql

I have 2 similar tables, one with all the data and the other contains a subset of the first. Every 2-3 days I need to insert in the second table the missing values, and I use this code
INSERT INTO [SRVDB2].[dbt].[curve].[curve_value]
SELECT *
FROM [SRVDB1].[dbt].[curve].[curve_value] as DB01
WHERE TargetDate >= '20150505'
and NOT EXISTS (SELECT *
FROM [SRVDB2].[dbt].[curve].[curve_value] as DB02
WHERE DB02.TargetDate = DB01.TargetDate
and DB02.[Hour] = DB01.[Hour]
and DB02.[id_Mkt] = DB01.[id_Mkt]
and DB02.[Price] = DB01.[Price]
and DB02.VoSe = DB01.VoSe
and DB02.VoBu = DB01.VoBu
)
It always worked but now I have some rows with NULL in column VoSe or VoBu and those values are not inserted correctly (even if executing only the SELECT statement seems to return all the differences). How can I handle these?

Add explicit check for NULL for both of these columns:
INSERT INTO [SRVDB2].[dbt].[curve].[curve_value]
SELECT *
FROM [SRVDB1].[dbt].[curve].[curve_value] as DB01
WHERE TargetDate >= '20150505'
and NOT EXISTS (SELECT *
FROM [SRVDB2].[dbt].[curve].[curve_value] as DB02
WHERE DB02.TargetDate = DB01.TargetDate
and DB02.[Hour] = DB01.[Hour]
and DB02.[id_Mkt] = DB01.[id_Mkt]
and DB02.[Price] = DB01.[Price]
and ((DB02.VoSe IS NULL AND DB01.VoSe IS NULL) OR DB02.VoSe = DB01.VoSe)
and ((DB02.VoBu IS NULL AND DB01.VoBu IS NULL) OR DB02.VoBu = DB01.VoBu)
)

#dotnetom's answer (+1) should work for your problem. However, making some assumptions on the problem you describe, I suspect the following would work just as well:
INSERT INTO [SRVDB2].[dbt].[curve].[curve_value]
SELECT *
FROM [SRVDB1].[dbt].[curve].[curve_value]
WHERE TargetDate >= '20150505'
EXCEPT SELECT *
FROM [SRVDB2].[dbt].[curve].[curve_value]

Use ISNULL to handle the NULL values.
NOTE: Use some random value in ISNULL function which will not come in those two columns. For example i have kept 'AAA'
SELECT *
FROM [SRVDB1].[dbt].[curve].[curve_value] as DB01
WHERE TargetDate >= '20150505'
and NOT EXISTS (SELECT *
FROM [SRVDB2].[dbt].[curve].[curve_value] as DB02
WHERE DB02.TargetDate = DB01.TargetDate
and DB02.[Hour] = DB01.[Hour]
and DB02.[id_Mkt] = DB01.[id_Mkt]
and DB02.[Price] = DB01.[Price]
and ISNULL(DB02.VoSe,'AAA') = ISNULL(DB01.VoSe,'AAA')
and ISNULL(DB02.VoBu,'AAA') = ISNULL(DB01.VoBu,'AAA')
)

Related

Conditional statements in "WHERE" in SQL Server

What I want to achieve is to have a switch case in the where clause. I want to test if this statement returns something, if it returns null, use this instead.
Sample:
SELECT [THIS_COLUMN]
FROM [THIS_TABLE]
WHERE (IF THIS [ID] RETURNS NULL THEN DO THIS SUBQUERY)
What I mean is that it will do this query first.
SELECT [THIS_COLUMN]
FROM [THIS_TABLE]
WHERE [ID] = 'SOMETHING'
If this returns NULL, do this query instead:
SELECT [THIS_COLUMN]
FROM [THIS_TABLE]
WHERE ID = (SELECT [SOMETHING] FROM [OTHER_TABLE]
WHERE [SOMETHING_SPECIFIC] = 'SOMETHING SPECIFIC')
Note that the expected results from the intended query varies from 30 rows up to 15k rows. Hope it helps.
Adding more information:
The results for this query will be used for another query but will just focus on this query.
Providing a real case scenario:
[THIS_COLUMN] is expected to have a list of VALUES.
[THIS_TABLE] contains the latest data only(let's say 1 year's worth of data) while the [OTHER_TABLE] contains the historical data.
What I want to achieve is when I query for a data that is not with in the 1 year's worth of data, IE 'SOMETHING' is not with in the 1 year scope(or in my case it returns NULL), I will use the other query where I query the 'SOMETHING_SPECIFIC'(Or may be 'SOMETHING' from the first statement makes more sense) from the historical table.
If I as reading through the lines correctly, this might work:
SELECT THIS_COLUMN
FROM dbo.THIS_TABLE TT
WHERE TT.ID = 'SOMETHING'
OR TT.ID = (SELECT OT.SOMETHING
FROM dbo.OTHER_TABLE OT
WHERE OT.SOMETHING_SPECIFIC = 'SOMETHING SPECIFIC'
AND NOT EXISTS (SELECT 1
FROM dbo.THIS_TABLE sq
WHERE sq.ID = 'SOMETHING'
AND THIS_COLUMN IS NOT NULL))
Note, however, that this could easily not be particularly performant.
You an use union all and not exists:
select this_column
from this_table
where id = 'something'
union all
select this_column
from this_table
where
not exists (select this_column from this_table where id = 'something')
and id = (select something from other_table where something_specific = 'something specific')
The first union member attempts to find rows that match the first condition, while the other one uses the subquery - the not exists prevents the second member to return something if the first member found a match.
90% of the time you can use a query-batch (i.e. a sequence of T-SQL statements) in a single SqlCommand object or SQL Server client session, so with that in-mind you could do this:
DECLARE #foo nvarchar(50) = (
SELECT
[THIS_COLUMN]
FROM
[THIS_TABLE]
WHERE
[ID] = 'SOMETHING'
);
IF #foo IS NULL
BEGIN
SELECT
[THIS_COLUMN]
FROM
[THIS_TABLE]
WHERE
[ID] = (
SELECT
[SOMETHING]
FROM
[OTHER_TABLE]
WHERE
[SOMETHING_SPECIFIC] = 'SOMETHING SPECIFIC'
)
END
ELSE
BEGIN
SELECT #foo AS [THIS_COLUMN];
END
That said, SELECT ... FROM ... WHERE x IN ( SELECT y FROM ... ) is a code-smell in a query - you probably need to rethink your solution entirely.

select subquery using data from the select statement?

I have two tables, headers and lines. I need to grab the batch_submission_date from the header table, but sometimes a query for batch_id will return a null for batch_submission_date, but will also return a parent_batch_id, and if we query THAT parent_batch_id as a batch_id, it will then return the correct batch_submission_date.
e.g.
SELECT t1.batch_id,
t1.parent_batch_id,
t2.batch_submission_date
FROM db.headers t1, db.lines t2
WHERE t1.batch_id = '12345';
output = 12345, 99999, null
Then we use that parent batch_id as a batch_id :
SELECT t1.batch_id,
t1.parent_batch_id,
t2.batch_submission_date
FROM db.headers t1, db.lines t2
WHERE t1.batch_id = '99999';
and we get output = 99999,99999,'2018-01-01'
So I'm trying to write a query that will do this for me - anytime a batch_id's batch_submission_date is null, we find that batch_id's parent batch_id and query that instead.
This was my idea - but I just get back null both for bp_batch_submission_date and for new_submission_date.
SELECT
t1.parent_id as parent_id,
t1.BATCH_ID as bp_batch_id,
t2.BATCH_LINE_NUMBER as bp_batch_li,
t1.BATCH_SUBMISSION_DATE as bp_batch_submission_date,
CASE
WHEN t1.BATCH_SUBMISSION_DATE is null
THEN
(SELECT a.BATCH_SUBMISSION_DATE
FROM
db.headers a,
db.lines b
WHERE
a.SD_BATCH_HEADERS_SKEY = b.SD_BATCH_HEADERS_SKEY
and a.parent_batch_id = bp_batch_id
and b.batch_line_number = bp_batch_li
) END as new_submission_date
FROM
db.headers t1,
db.lines t2
WHERE
t1.SD_BATCH_HEADERS_SKEY = t2.SD_BATCH_HEADERS_SKEY
and (t1.BATCH_ID = '12345' or t1.PARENT_BATCH_ID = '12345')
and t2.BATCH_LINE_NUMBER = '1'
GROUP BY
t2.BATCH_CLAIM_LINE_STATUS_DESC,
t1.PARENT_BATCH_ID,
t1.BATCH_ID,
t2.BATCH_LINE_NUMBER,
t1.BATCH_SUBMISSION_DATE;
is what I'm trying to do possible? using the bp_batch_id and bp_batch_li variables
Use CTE (common table expression) to avoid redundant code, then use coalesce() to find parent date in case of null. In your first queries you didn't attach joining condition between two tables, I assumed it's based on sd_batch_headers_skey like in last query.
dbfiddle demo
with t as (
select h.batch_id, h.parent_batch_id, l.batch_submission_date bs_date
from headers h
join lines l on l.sd_batch_headers_skey = h.sd_batch_headers_skey
and l.batch_line_number = '1' )
select batch_id, parent_batch_id,
coalesce(bs_date, (select bs_date from t x where x.batch_id = t.parent_batch_id)) bs_date
from t
where batch_id = 12345;
You could use simpler syntax with connect by and level <= 2 but if in your data there are really rows containing same ids (99999, 99999) then we get cycle error.

How do I UPDATE from a SELECT in Informix?

I'm trying to do an update using data from another table. I've tried this answer (the second part), but it is not working for me. I'm receiving a generic error message of syntax error.
I've also tried this solution and received a syntax error message too.
If I try to update just one column, it works:
UPDATE dogs
SET name =
(
SELECT 'Buddy'
FROM systables
WHERE tabid = 1
);
But I need to update multiples columns. Unfortunately, this is not working:
UPDATE dogs
SET (name, breed) =
(
SELECT 'Buddy', 'pug'
FROM systables
WHERE tabid = 1
);
Informix version is 12.10.FC8
You are missing 1 more set of parentheses around the subquery.
From the Informix manual:
The subquery must be enclosed between parentheses. These parentheses
are nested within the parentheses that immediately follow the equal (
= ) sign. If the expression list includes multiple subqueries, each subquery must be enclosed between parentheses, with a comma ( , )
separating successive subqueries:
UPDATE ... SET ... = ((subqueryA),(subqueryB), ... (subqueryN))
The following examples show the use of subqueries in the SET clause:
UPDATE items
SET (stock_num, manu_code, quantity) =
(
(
SELECT stock_num, manu_code
FROM stock
WHERE description = 'baseball'
),
2
)
WHERE item_num = 1 AND order_num = 1001;
UPDATE table1
SET (col1, col2, col3) =
(
(
SELECT MIN (ship_charge), MAX (ship_charge)
FROM orders
),
'07/01/2007'
)
WHERE col4 = 1001;
So in order for your update to be accepted by Informix it has to be:
UPDATE dogs
SET (name, breed) =
(
(
SELECT 'Buddy', 'pug'
FROM systables
WHERE tabid = 1
)
);

How to add results of a SELECT to a table

I have a SQL select statement which is comparing two tables. I am getting the values where the rows are the same. Now I have got this in the procedure I need to add these into a new table (coftReconciliationMatches). The table has all the same columns but one additional one 'MatchOrNoMatch'. I need to pass through the values of the row that are matched and also need to pass through 'Match' to the column 'MatchOrNoMatch'
This is the current part of the SQL script I have;
SELECT *
FROM dbo.coftReconciliationFileInfo AS a
WHERE EXISTS (SELECT *
FROM dbo.coftPreReconciliationInfo AS b
WHERE a.Rec_PK = b.Rec_PK
AND a.ExtRef1 = b.ExtRef1
AND a.SedolRef = b.SedolRef
AND a.ValLatest = b.ValLatest
AND a.Totunits = b.Totunits
AND a.FundsOrCash = b.FundsOrCash )
When comparing a lot of columns, the SELECT INTERSECT and SELECT EXCEPT commands can save you a lot of effort:
INSERT dbo.coftReconcilliationMatches
(Rec_PK, ExtRef1, SedolRef, ValLatest, Totunits, FundsOrCash, MatchOrNoMatch)
select Rec_PK, ExtRef1, SedolRef, ValLatest, Totunits, FundsOrCash, 'Match'
from dbo.coftReconciliationFileInfo
intersect select Rec_PK, ExtRef1, SedolRef, ValLatest, Totunits, FundsOrCash, 'Match'
from dbo.coftPreReconciliationFileInfo
(Check for typos!)
If you are creating the table on the fly (something I wouldn't recommend doing), you'd use SELECT INTO.
INSERT INTO [table] ([col1], [col2]) SELECT colx, ...
Use Select Into for this. See here http://www.w3schools.com/sql/sql_select_into.asp
Try something like:
INSERT INTO <your table> (<list of columns>)
SELECT <list of columns from select>, '<the additional column>' FROM dbo.coftReconciliationFileInfo AS a
WHERE EXISTS(SELECT * FROM dbo.coftPreReconciliationInfo AS b
WHERE a.Rec_PK = b.Rec_PK AND a.ExtRef1 = b.ExtRef1 AND a.SedolRef = b.SedolRef AND a.ValLatest = b.ValLatest AND a.Totunits = b.Totunits AND a.FundsOrCash = b.FundsOrCash )
also see http://www.1keydata.com/sql/sqlinsert.html
you need to do:
INSERT INTO Table (column1, column2..)
(SELECT column1, column2 ....
FROM dbo.coftReconciliationFileInfo AS a
WHERE EXISTS (SELECT *
FROM dbo.coftPreReconciliationInfo AS b
WHERE a.Rec_PK = b.Rec_PK
AND a.ExtRef1 = b.ExtRef1
AND a.SedolRef = b.SedolRef
AND a.ValLatest = b.ValLatest
AND a.Totunits = b.Totunits
AND a.FundsOrCash = b.FundsOrCash ))

return a default record from a sql query

I have a sql query that I run against a sql server database eg.
SELECT * FROM MyTable WHERE Id = 2
This may return a number of records or may return none. If it returns none, I would like to alter my sql query to return a default record, is this possible and if so, how? If records are returned, the default record should not be returned. I cannot update the data so will need to alter the sql query for this.
Another way (you would get an empty initial rowset returned);
SELECT * FROM MyTable WHERE Id = 2
IF (##ROWCOUNT = 0)
SELECT ...
SELECT TOP 1 * FROM (
SELECT ID,1 as Flag FROM MyTable WHERE Id = 2
UNION ALL
SELECT 1,2
) qry
ORDER BY qry.Flag ASC
You can have a look to this post. It is similar to what you are asking
Return a value if no rows are found SQL
I hope that it can guide you to the correct path.
if not exists (SELECT top 1 * FROM mytable WHERE id = 2)
select * from mytable where id= 'whatever_the_default_id_is'
else
select * from mytable where id = 2
If you have to return whole rows of data (and not just a single column) and you have to create a single SQL query then do this:
Left join actual table to defaults single-row table
select
coalesce(a.col1, d.col1) as col1,
coalesce(a.col2, d.col2) as col2,
...
from (
-- your defaults record
select
default1 as col1,
default2 as col2,
...) as d
left join actual as a
on ((1 = 1) /* or any actual table "where" conditions */)
The query need to return the same number of fields, so you shouldn't do a SELECT * FROM but a SELECT value FROM if you want to return a default value.
With that in mind
SELECT value FROM MyTable WHERE Id = 2
UNION
SELECT CASE (SELECT count(*) FROM MyTable WHERE Id = 2)
WHEN 0 THEN 'defaultvalue'
END