I have the following scenario:
Database A.table A.name
Database A.table A.Application
Database B.table B.name
Database B.table B.Application
Database C.table C.name
Database C.table C.Application
I'm trying to write an UPDATE query that will set a value to table A.Application. The value I need to update it with could come from tables B or C but not both; A.name only exists in either B or C. The condition for each row I would need to update on would be as so:
If B.name exists for A.name, set A.Application = B.application
If C.Name exists for A.name, set A.application = C.application
I'm trying to do this non-dynamically; any assistance would be appreciated.
You can do it in two statements:
UPDATE A
SET A.Application = B.Application
FROM A
INNER JOIN B ON A.name = B.name;
UPDATE A
SET A.Application = C.Application
FROM A
INNER JOIN C ON A.name = C.name;
Only one of them will actually do anything to the data, assuming the names in B and C are truly orthogonal. Otherwise, C wins.
Or you could get fancy (without having actually tried it):
UPDATE A
SET A.Application = ISNULL(B.Application, C.Application)
FROM A
LEFT JOIN B ON A.name = B.name
LEFT JOIN C ON A.name = C.name
declare #A table([name] varchar(1),[Application] int)
insert #A
select 'a',0 union all
select 'b',0 union all
select 'c',0
declare #B table([name] varchar(1),[Application] int)
insert #B
select 'a',5 union all
select 'b',6
declare #C table([name] varchar(1),[Application] int)
insert #C
select 'c',8
update #A set [Application]=b.[Application]
from #A a left join
(
select [name],[Application] from #B
union all
select [name],[Application] from #C
) b on a.name=b.name
select * from #A
/*
name Application
---- -----------
a 5
b 6
c 8
*/
Related
I have two fields with an email address in #data table that I am trying to join on. I want it to join on rep email address and if that doesn't work, I want it to join on email. I tried running the following query:
select a.*
from #data a
join #email b on b.email=coalesce(a.rep_email_address,a.email)
where a.rep_email_address<>a.email
This however doesn't work, because in the case where a.rep_email_adress is not null but doesn't match with b.email, it will drop the record instead of taking the a.email field.
This is the work-around I found:
select a.*
from #data a
join #email b on a.email=b.email
except
select a.*
from #data a
join #email b on a.rep_email_address=b.email
union
select a.*
from #data a
join #email b on a.rep_email_address=b.email
where a.rep_email_address<>a.email
This however, is far from optimal, so I am wondering- any way to write this to perform better/look cleaner or simpler? Just to clarify- this query works (the second query), I am wondering if there is a better way to write it.
Thank you!
This should be much simpler. However, I also recommend you check the execution plan on this query to help you analyze if this is more optimal. [Or just compare the resulting execution times on your tables]
SELECT a.*
FROM #data a
JOIN #email b
ON (a.rep_email_address = b.email
OR a.email = b.email )
WHERE a.rep_email_address<>a.email;
# Not sure why or IF you need this where clause specifically.
Try it like this...
SELECT
a.*,
SomeColumn = ISNULL(e1.SomeColumn, e2.SomeColumn)
from
#data a
LEFT JOIN #email e1
ON e1.email = a.rep_email_address
LEFT JOIN #email e2
ON e2.email = a.email
AND e1.email IS NULL
WHERE
a.rep_email_address <> a.email
AND (
e1.email IS NOT NULL
OR
e2.email IS NOT NULL
);
HTH, Jason
SQL sever uses "Three-Valued Logic" in boolean evaluations:
NULL <> 2 --> Unknown (in your case in the where clause it will essentially become false)
NULL <> NULL --> Unknown (same as above)
a <> b --> true
In your case your original query should be:
select a.*
from #data a
join #email b on b.email=coalesce(a.rep_email_address,a.email)
where ISNULL( a.rep_email_address, '' ) <> ISNULL( a.email, '' )
If you care about performance, then try to avoid the use of functions in join predicates or WHERE conditions as this prevents SQL Server from using indexes on columns that are passed into the function.
SELECT a.*
FROM #data AS a
INNER JOIN #email AS b ON b.email = a.rep_email_address OR b.email = a.email
WHERE a.rep_email_address <> a.email OR ( a.rep_email_address IS NULL OR a.email IS NULL )
Summary:
Do not use NULLs to denote empty strings as this requires a lot of extra code to then check for NULLs
In my query I have to join tables from db that is not under my control. It is driving me mad as sometimes this db is not accessible (please don't ask me why) and this breaks my query. Fields I'm joining are not fundamental for my operations and I want my app to work normally even if these fields are not accessible at a time.
Here's the data structure that I do not own:
[DBOutOfControl].[dbo].[Table1]:
[Field1]
[Field2]
[DBOutOfControl].[dbo].[Table2]:
[Field1]
[Field2]
[Field3]
And here is my table:
[DBInMyControl].[dbo].[Table3]:
[Field1]
My original query looks something like that:
SELECT [Table3].[MyID],
[ForeignDataQry].[A],
[ForeignDataQry].[B]
FROM [DBInMyControl].[dbo].[Table3]
LEFT JOIN
(SELECT [Table1].[Field1] AS [MyID],
[Table1].[Field2] AS [A],
[SubQry].[Field2] AS [B]
FROM [DBOutOfControl].[dbo].[Table1]
LEFT JOIN
(SELECT [Table2].[Field1],
[Table2].[Field2]
FROM [DBOutOfControl].[dbo].[Table2]
WHERE [Table2].[Field3] = 'Where') AS [SubQry] ON [Table1].[Field1] = [SubQry].[Field1]) AS [ForeignDataQry] ON [Table3].[MyID]=[ForeignDataQry].[MyID]
How can i bullet-proof this query so when [ForeignDataQry] generates an error the result would be:
[MyID] [A] [B]
1 NULL NULL
Otherwise
[MyID] [A] [B]
1 Va1 Val2
Is there something that could be done server side?
Just specify the expected result of COUNT, the three names, and you can check tables beforehand. A minor rewrite can allow this to check for objects other than tables, utilize EXISTS if desired, skip or add more checks, etc.:
IF 0 = ( -- Specify how many records you expect to come.
SELECT COUNT(C.[name]) AS [COUNT]
FROM sys.objects AS O
LEFT JOIN sys.schemas AS S ON S.schema_id = O.schema_id
LEFT JOIN sys.columns AS C ON C.object_id = O.object_id
WHERE O.[name] = 'tablename'
AND S.[name] = 'schemaname'
AND C.[name] = 'columnname'
)
SELECT 1 AS A -- Do some code.
ELSE
SELECT 2 AS B -- Do some other code.
I'd wrap the problematic query in dynamic code in order to be able to catch the compilation error (that we cannot catch in the same scope) like this:
begin try
declare #sql varchar(4000) =
'SELECT [Table3].[MyID],
[ForeignDataQry].[A],
[ForeignDataQry].[B]
FROM [DBInMyControl].[dbo].[Table3]
LEFT JOIN
(SELECT [Table1].[Field1] AS [MyID],
[Table1].[Field2] AS [A],
[SubQry].[Field2] AS [B]
FROM [DBOutOfControl].[dbo].[Table1]
LEFT JOIN
(SELECT [Table2].[Field1],
[Table2].[Field2]
FROM [DBOutOfControl].[dbo].[Table2]
WHERE [Table2].[Field3] = ''Where'') AS [SubQry] ON [Table1].[Field1] = [SubQry].[Field1]) AS [ForeignDataQry] ON [Table3].[MyID]=[ForeignDataQry].[MyID]'
exec(#sql)
end try
begin catch
SELECT [Table3].[MyID],
cast(null as ... )as [A],
cast(null as ...) as [B]
FROM [DBInMyControl].[dbo].[Table3]
end catch
Here I use cast(null as ... )as [A] to get the same type as [ForeignDataQry].[A] has, for example if [ForeignDataQry].[A] is int there should be int: cast(null as int )as [A]
I dealt with this problem in a different way that I originally wanted but anyway:
I created a [Table4] that keeps copy of the records from foreign tables - fields matches [ForeignDataQry] + timestamp. I created procedure:
CREATE PROCEDURE [dbo].[CopyForeignData]
AS
DECLARE #Timestamp datetime
SET #Timestamp = getdate()
BEGIN
INSERT INTO [DBInMyControl].[dbo].[Table4] ([MyID], [A], [B], [Timestamp])
SELECT [Table1].[Field1] AS [MyID],
[Table1].[Field2] AS [A],
[SubQry].[Field2] AS [B],
#Timestamp
FROM [DBOutOfControl].[dbo].[Table1]
LEFT JOIN
(SELECT [Table2].[Field1],
[Table2].[Field2]
FROM [DBOutOfControl].[dbo].[Table2]
WHERE [Table2].[Field3] = 'Where') AS [SubQry] ON [Table1].[Field1] = [SubQry].[Field1]
DELETE FROM [DBInMyControl].[dbo].[Table4] WHERE [Timestamp] <> #Timestamp
END
I will call this every time when I start my app and handle error there and modify my main LEFT JOIN to refer [Table4]
My tables are defined like below:
#TempData(ColmnA, ColumnB) -- Temp table.
EmployeeDSU(ColumnA, ColumnB, ColumnC, ColumnD, ColumnE)
#TempData is coming from .csv file and columns may change.
Now, what I want to do is:
If ColumnA, ColumnB exists in EmployeesDSU table, then the #TempData data should be inserted into EmployeesDSU table, and for all the remaining columns in EmployeesDSU table, NULL should be inserted. I should do this everything in Stored procedure.
Can anybody please suggest me how to do!
if object_id('tempdb..#TempData') is not null drop table #TempData;
select 1 ColumnA, 1 ColumnB into #TempData;
if not exists(
select
*
from
(
select
c.name
from
tempdb.sys.columns c
where
c.object_id = object_id('tempdb..#TempData')
) a
left join (
select
*
from
sys.columns c
where
c.object_id = object_id('dbo.EmployeeDSU')
) b on a.name = b.name
where
b.name is null
) begin
insert into dbo.EmployeeDSU(ColumnA, ColumnB)
select
t.ColumnA, t.ColumnB
from
#TempData t
;
end;
I have two tables:
TableA:
ID Values
---------------
1 Q
2 B
3 TA
4 BS
TableB:
RawValue Value
------------------
[1][4] QBS
[2][1][3] BQTA
I need to generate TableB values with its given RawValues. each [X] in rawvalue is the ID coulmn of TableA and shoud be replace with its value .
[1][4] means that Value of TableA with has ID of 1 (Q) and Value of TableA with has ID of 4 (BS) then should equal to QBS.
can anyone suggest a way to do it?
this is what I have already tried:
update tableb set value=replace(rawvalue,'[' + (select id from tablea where id = cast(replace(replace(rawdata,'[',''),']','') as int)) + ']',
(select values from tablea where id = cast(replace(replace(rawdata,'[',''),']','') as int)))
By the way: this is still in test process and I can totally change tables, rowvalue format and replacement methods if anyone has a better idea.
declare #tableA table (id int, value varchar(50))
insert into #tableA (id, value)
select 1, 'Q' union all
select 2, 'B' union all
select 3, 'TA' union all
select 4, 'BS'
declare #tableB table (rawdata varchar(255), value varchar(255))
insert into #tableB (rawdata)
select '[1][4]' union all -- QBS
select '[2][1][3]' -- BQTA
update b
set value = (
select a.value + ''
from #tableA a
cross apply (select charindex ('[' + cast (a.id as varchar(50)) + ']', b.rawdata) as pos) p
where pos > 0
order by pos
for xml path('')
)
from #tableB b
select * from #tableB
P.S. I would recommend not to name field similar to reserved keywords (I mean Values).
Turn RawValue into XML, shred the XML to get one row for each value in RawValue and join to TableA to get the value.
Use the for xml path() trick to concatenate the values from TableA.
update TableB
set Value = (
select T.Value as '*'
from (
select row_number() over(order by T2.X) as SortOrder,
TableA.Value
from (select cast(replace(replace(TableB.RawValue, '[', '<x>'), ']', '</x>') as xml)) as T1(X)
cross apply T1.X.nodes('x') as T2(X)
inner join TableA
on TableA.ID = T2.X.value('text()[1]', 'int')
) as T
order by T.SortOrder
for xml path('')
)
SQL Fiddle
I'm trying to filter out a table using filter in the outer join clause rather than a where clause. When i try to do so i'm getting unexpected results. The whole table is returned as though i didn't apply the filter at all.
When I run this example, i get different results for the last two queries. I would expect them to have the same results but it's not the case. What is going on here?
declare #a table
(
id int
,content varchar(100)
)
declare #b table
(
id int
,content varchar(100)
)
insert into #a (id,content) values (1,'Apple')
insert into #a (id,content) values (2,'Banana')
insert into #a (id,content) values (3,'Orange')
insert into #b (id,content) values (1,'Juice')
insert into #b (id,content) values (2,'Peel')
insert into #b (id,content) values (3,'Julius')
--basic outer join
select * from #a a left join #b b on a.id=b.id
--outer join with where clause filter
select * from #a a left join #b b on a.id=b.id where a.id=1
--outer join with join clause filter
select * from #a a left join #b b on a.id=1 and a.id=b.id
An outer join is allowed to return NULL for the joined table's row, whereas a where clause must be matched before the result is returned.
select * from #a a left join #b b on a.id=b.id where a.id=1
translates to "Give me all rows from a where id=1 and try to correlate this with any rows in b where a.id=b.id.
select * from #a a left join #b b on a.id=1 and a.id=b.id
on the other hand, translates to "Give me all rows from a and, if a.id=1, try to correlate this with any rows in b where a.id=b.id (otherwise just give me the data from a).
Contrast this with an inner join, where adding a condition to the ON clause and adding it to the WHERE clause is synonymous.