Alias a database name within the query as to avoid dynamic SQL? - sql-server

We have 4 distinct environments for one of our applications through our Dev to Prod pipeline, each having two databases. Within these databases there are several queries referencing the other database. So for example we may have this:
select a.name, b.description
FROM Database1.dbo.Names a
INNER join Database2.dbo.Descriptions b on a.ID = b.ID;
Just an example, not an actual query from our system... but hopefully you get the point. All four environments live on their own SQL servers so I can gracefully move code from bottom to top without any changes.
Now we're looking at possibly combining the bottom three environments on to the same SQL Server Instance (Prod will keep its own server), so all six databases will be on the same server together. In doing this the database names will have a suffix of their type, so Database1_Dev and Database2_Dev for example.
Well there lies the issue, all queries using just Database1 and Database2 will now break, and if I do change them to use Database1_Dev and Database2_Dev at the lowest level I can't just move them up to the next level Database1_Sandbox and Database2_Sandbox without modifying the queries.
The options I'm looking at are requesting three SQL instances on this server - which I'm sure will get shot down - or changing the queries to use dynamic SQL where I can pass in the DB name as a variable. I don't like either option.
Synonyms are the only thing I can think of that would allow a different name to be used, but this is going the wrong direction. I don't need one DB to have multiple names, rather multiple databases to have the same name within the same context. So like if in a procedure I could pass in the environment and I could alias Database1 to Database1_Sandbox just within the transaction or query. I don't think there is any way to do this, but I thought I'd ask.
Thanks for any suggestions.

Related

How to access LinkedServer on another database in SQL Server

I want to know how to access LinkedServer of databaseA from databaseB in SQL Server.
I need to know sample code. Thanks.
If the servers are already linked, are both running SQL Server (you're not linking to a different kind of database completely), and you are connected to databaseB with a login having appropriate permissions, here is an example:
SELECT <columns>
FROM ServerName.databaseA.schema.Table alias
WHERE alias.Column = 'SomeValue'
Note you need to use all four parts of the name (server, database, schema, object), and therefore you will definitely want to use a table alias. The schema is practically almost always dbo, and can often be elided with just an extra . ( FROM ServerName.databaseA..Table).
But as long as you get the name right, you can use items in databaseA as if they were there in B, including for JOINs and similar. However, performance can be poor, because the server for databaseB must put together an execution plan without knowing anything about indexes or statics from databaseA.
If the databaseA is something other than SQL Server, you will need to use OPENQUERY(). Like the first option, OPENQUERY() results can be given an alias and then used with JOIN, APPLY, subquery, etc, and the same warning about performance applies.

SQL Server How to add a linked server to the same instance without performance impact

in my company, we have several environments with MS SQL database servers (SQL 2008 R2, SQL 2014). For the sake of simplicity, let us consider just a TEST environment and a PROD environment and two sql servers in each. Let the servers be called srTest1, srTest2, srProd1, srProd2 and each be running a default MS SQL Server instance. We work with multiple databases, say DataDb, ReportDb, DWHDb.
We want to keep the same source code in T-SQL for both TEST and PROD, but the problem is the architecture or distribution of the above mentioned databases in each environment:
TEST:
srTest1 - DataDb
srTest2 - DWHDb, ReportDb
PROD:
srProd1 - DataDb, ReportDb
srProd2 - DWHDb
Now, say, in ReportDb, we write stored procedures with many SELECTs referencing tables and other objects in DataDb and DWHDb. In order to have source code as universal as possible, we decided to create linked servers for each database on each db server in each environment and name them with respect to the database they're created for. Therefore, there'll be these linked servers:
lnkDataDb, lnkReportDb and lnkDWHDb on srTest1,
lnkDataDb, lnkReportDb and lnkDWHDb on srTest2,
lnkDataDb, lnkReportDb and lnkDWHDb on srProd1,
lnkDataDb, lnkReportDb and lnkDWHDb on srProd2.
And we'll adjust the source in the stored procs accordingly. For instance:
Instead of
SELECT * FROM DataDb.dbo.Contact
We'll write
SELECT * FROM lnkDataDb.DataDb.dbo.Contact
The example above is reasonable for a situation where the database from which you execute the query (ReportDb) lies on a different server than that with the referenced table (DataDb). Which is the case for the TEST environment. But not so in PROD. It is performance I'm here concerned about. The SQL Server will treat that SELECT as a "remote query" no matter whether, in fact, it is a reference to a local object or not.
Now, it comes the most important part:
If you check these 3 queries for their actual execution plans, you'll see an interesting thing:
(1) SELECT * FROM DataDb.dbo.Contact
(2) SELECT * FROM srProd1.DataDb.dbo.Contact
(3) SELECT * FROM lnkDataDb.DataDb.dbo.Contact
The first two (query #1 and #2) have the same execution plan (the fastest possible) even if you use the four-part name manner of referencing the table Contact in #2.
The last query has a different plan (remote query, thus slower).
The question is:
Can you somehow create a linked server to self (the same sql server instance, the default instance actually) as an "alias" to the name of the host (srProd1) in order for the SQL server to be forced to understand it as local and not issue "remote execution" plans?
Thanks a lot for any hints
Pavel
Recently I found a workaround which seems to solve this kind of issues more efficiently and more elegantly than the solution with self-pointing linked servers.
If you work (making reports, for example) with multiple databases on multiple SQL servers and the physical distribution of the databases on the servers is a challenge since it may differ from one environment to another (e.g. TEST vs PROD), I suggest this:
Use three-part db object names whenever possible. If the objects are local, then execution plans are also local, and thus effective.
Example:
SELECT * FROM DataDb.dbo.Contact
If you happen to run the above query from within a different SQL server instance (residing on a different physical machine, for example, but this not necessarily, the other SQL server instance could be installed even on the same machine), briefly if you're about to use a four-part name:
SELECT * FROM lnkDataDb.DataDb.dbo.Contact
Then you can circumvent that using the following trick:
Let's assume lnkDataDb points to srTest2 and you're executing your queries from srTest1. Now, you'll create a "fake" database DataDb on your local server (srTest1). This fake DataDb shall contain no real db objects (no tables, no views, no stored procedures, no UDFs etc.). There shall only be synonyms defined in it. (And there also shall be the same schemas in it as those in the real DataDb on srTest2). These synonyms shall be named exactly the same way as their real db-object counterparts in DataDb on srTest2. Example:
-- To be executed on srTest1.
EXEC sp_addlinkedserver
#server = N'lnkDataDb',
#srvproduct = N'',
#provider = N'SQLNCLI',
#datasrc = N'srTest2'
;
GO
CREATE DATABASE [DataDb];
GO
USE [DataDb];
GO
CREATE SYNONYM dbo.Contact FOR lnkDataDb.DataDb.dbo.Contact;
GO
Now, if you want to SELECT rows from the table dbo.Contact residing in the database DataDb on srTest2 and you're executing your query from srTest1, you'll use a simple three-part table name:
SELECT * FROM DataDb.dbo.Contact
Of course, on srTest1, this is not a table, that's just a synonym referencing the same-named table on srTest2. However, that's the trick, you use the same query syntax as if you were executing it on srTest2 where the real db object resides.
There are disadvantages of this approach:
On the local server, at the beginning, there must not be a database
with the same name as the remote one. Because you're about to create
a "fake" database with that name to reflect the names of remote
db objects.
You're creating one database that is almost empty, thus
increasing the mess of various databases residing on your local
SQL server. This might provoke reluctance of your database admin
if they prefer having as few databases as possible.
If you're developing your T-SQL scripts in SQL Server Management
Studio, for example, using synonyms cuts you off from the convenience
of the IntelliSense feature.
Advantages outweigh the above-mentioned disadvantages, though:
Your scripts work in any environment (DEV, TEST, PROD) without
the need to change any part of the source code.
If the other database you're querying data from resides on the same
SQL server instance as your script, you also use the three-part name
convention and your SQL server evaluates the query in execution plan
as local which is OK. (This is what the original question of this
post was searching to solve.)
If the other database you're querying data from resides on another
SQL server instance, you still use a "local syntax manner" of a SQL
query (with the synonym) which, only at runtime, evaluates in
a remote execution plan. Which is also fine because the db object
actually is remote.
To summarize
The query executes as local if the referenced object is local, the query executes as remote if the referenced object is remote, but the T-SQL script is always the same. You don't have to change a letter in it.

How do I query tables located in different database?

My original question was about whether to keep separate ASPNETDB.MDF from the application database or merge all the tables in one database. Checking the previous questions/answers, I learned that it depends on whether the membership data would be shared across several applications.
Now, my question is this. In case I decide to keep ASPNETDB.MDF separate from the application DB, how can I query 2 tables located in 2 different databases?
Thanks for helping.
If you have two databases/schemas on the same database server, you can query across databases with the following syntax:
select *
from database1.dbo.table1 t1 join database2.dbo.table2 t2 on
t1.field1 = t2.field2
If they are on physically separate servers, you can still do a cross-database query, but you need to link the servers first:
http://msdn.microsoft.com/en-us/library/aa213778(v=sql.80).aspx
You can check your SQL Server version by:
SELECT SERVERPROPERTY('Edition')
If you are using "SQL Azure" you will not be able to use a table from different database. You will get this error:
Reference to database and/or server name in 'DataBase.Schema.Table' is not supported in this version of SQL Server.
Even if you try to Query a different database from your file like this:
USE ANOTHER_DATABASE;
You will get this error:
USE statement is not supported to switch between databases. Use a new connection to connect to a different database.

Data retrieval and Join operations with cluster db server

If any database spreads across multiple servers (ex. Microsoft Sql Server), how can we do join or filter operations. In my scenario, if suppose:
A single table spreads across multiple servers how can we filter rows based on user input?
If master table is there on one db server and transaction table is at another db server, how can we do join operations?
Please let me know how can we achieve this and where can I get more details about this?
I think you're confused about SQL clustering - it doesn't allow you to split tables across multiple servers any differently than you would put different tables in different databases on the same server. Clustering is used for hot failover and redundancy.
However, I think I see what you're asking - if you want to split a database up between different physical servers, the easiest thing to do might be to have a VIEW that unifies those tables together in one place, and then you can query and filter that. SQL Server is smart enough (as long as there are indexes and statistics in place to make the decision from) to send the query where it needs to go if you select something from the unifying view.
For example, say you have two servers - SERVER1 and SERVER2 that both have a database - DATABASE - and each server has a table - TABLE - that has half the data in it (between the two servers, you have every row). Just create a view somewhere - either server, or somewhere else entirely - that looks like this, and then add Linked Servers for SERVER1 and SERVER2 that allow SQL Server to get the data from the remote location:
CREATE VIEW SomeView
AS
SELECT *
FROM SERVER1.DATABASE..TABLE
UNION
ALL
SELECT *
FROM SERVER2.DATABASE..TABLE
That way, you have one place to query and you'll always grab the data from whatever server it's on, instead of querying each server by itself. You can do this even if you don't want to split up individual tables - just create a view for each table you want to move, and have the view check whatever server the table is actually located on.
If I've missed your actual question, please leave a comment and some clarification and I'll be happy to add some more detail.

How do I join two tables from two different databases?

Is there any way to use a query and join two tables that is in two different database on the same server for DbVisualizer? I used the following for the SQL server
Select * from table union select * from datbase.dbo.table2
I tried this for the DbVisualizer, and it didnt work. How do I do this?
If the databases are in different servers you need to make sure that they are set up as linked servers.
Also be warned that the optimizer is relatively weak in this scenario, same server or not. The problem is that the statistics used for weighting costs of different operations aren't necessarily meaningful between different databases, especially at the point where the two databases will "intersect". So performance isn't what it could be.
If DBVisualizer supports views, manually setup a view of table2 in your database.
create view table2 as select * from database.dbo.table2
I dont think it can be done. I resolved the situation, by running a nightly data transfer to the SQL server. I do the union select from there...

Resources