Reputation: 57306
I have a number of SQL Server databases (different versions from 2012 to 2019). The schema in each one is very similar but not exactly the same. For example, there's table ORDERS
, which has about 50 columns - and one column is called differently in two different databases:
select p_user from orders
select userpk from orders
Note that I showed two databases above, but there are actually more than 20 - some are DB1 type, the others are DB2 type
I can't do much about these differences - they are historic - and changing the schema to match is not an option.
I want to be able to run the same SQL statement against all of these databases at once. I'd like to write the query in such a way that it would use one column if it exists and another if it doesn't. For example:
select
case
when COL_LENGTH('orders', 'p_user') IS NOT NULL
then
orders.p_user
else
orders.userpk
end
from orders
This unfortunately doesn't work, as SQL server seems to try to evaluate both results regardless of whether the condition is true or false. The same thing happens if I use IIF
function.
If I simply run
select
case
when COL_LENGTH('orders', 'p_user') IS NOT NULL
then
'orders.p_user'
else
'orders.userpk'
end
then I do get the correct string, which means my condition is correct.
How can I formulate the SQL statement to use one or the other column based on whether the first one exists?
Upvotes: 0
Views: 1988
Reputation: 57306
I ended up using dynamic sql like so:
declare @query nvarchar(1000)
set @query = concat(
'select count(distinct ', (case when COL_LENGTH('orders', 'p_user') IS NOT NULL then 'orders.p_user' else 'orders.userpk' end), ')
from orders'
);
execute sp_executesql @query
This solved my immediate issue.
Upvotes: 0
Reputation: 6788
create table orders1(colA int, colB int, colABC int);
insert into orders1 values(1, 2, 3);
go
create table orders2(colA int, colB int, colKLM int);
insert into orders2 values(5, 6, 7);
go
create table orders3(colA int, colB int, colXYZ int);
insert into orders3 values(10, 11, 12);
go
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders1 as o
) as src;
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders2 as o
) as src;
select colA, colB, vcolname as [ABC_KLM_XYZ]
from
(
select *,
(select o.* for xml path(''), elements, type).query('
/*[local-name() = ("colABC", "colKLM", "colXYZ")][1]
').value('.', 'int') as vcolname
from orders3 as o
) as src;
go
drop table orders1
drop table orders2
drop table orders3
go
Upvotes: 0
Reputation: 24136
If you can't change anything then your best (and maybe only) option is to use dynamic SQL. A query will only compile if all parts can be resolved at compile time (before anything runs) - which is why e.g. this will not compile:
IF COL_LENGTH('orders', 'p_user') IS NOT NULL THEN
select p_user from orders
ELSE
select userpk as p_user from orders
END
But this will work:
DECLARE @SQL NVARCHAR(MAX)
IF COL_LENGTH('orders', 'p_user') IS NOT NULL THEN
SET @SQL = 'select p_user from orders'
ELSE
SET @SQL = 'select userpk as p_user from orders'
END
EXEC (@SQL)
Upvotes: 2
Reputation: 1269463
Fix your tables by adding a computed column:
alter table db1..orders
add statuspk as (p_status);
(Or choose the other name.)
Then, your queries will just work without adding unnecessary complication to queries.
Upvotes: 0