Can someone please explain to me how SQL Server uses dot notation to identify
the location of a table? I always thought that the location is Database.dbo.Table
But I see code that has something else in place of dbo, something like:
DBName.something.Table
Can someone please explain this?
This is a database schema. Full three-part name of a table is:
databasename.schemaname.tablename
For a default schema of the user, you can also omit the schema name:
databasename..tablename
You can also specify a linked server name:
servername.databasename.schemaname.tablename
You can read more about using identifiers as table names on MSDN:
The server, database, and owner names are known as the qualifiers of the object name. When you refer to an object, you do not have to specify the server, database, and owner. The qualifiers can be omitted by marking their positions with a period. The valid forms of object names include the following:
server_name.database_name.schema_name.object_name
server_name.database_name..object_name
server_name..schema_name.object_name
server_name...object_name
database_name.schema_name.object_name
database_name..object_name
schema_name.object_name
object_name
An object name that specifies all four parts is known as a fully qualified name. Each object that is created in Microsoft SQL Server must have a unique, fully qualified name. For example, there can be two tables named xyz in the same database if they have different owners.
Most object references use three-part names. The default server_name is the local server. The default database_name is the current database of the connection. The default schema_name is the default schema of the user submitting the statement. Unless otherwise configured, the default schema of new users is the dbo schema.
What #Szymon said. You should also make a point of always schema-qualifying object references (whether table, view, stored procedure, etc.) Unqualified object references are resolved in the following manner:
Probe the namespace of the current database for an object of the specified name belonging to the default schema of the credentials under which the current connection is running.
If not found, probe the namespace of the current database for an object of the specified name belonging to the dbo schema.
And if the object reference is to a stored procedure whose name begins with sp_, it's worse, as two more steps are added to the resolution process (unless the references is database-qualified): the above two steps are repeated, but this time, looking in the database master instead of the current database.
So a query like
select *
from foo
requires two probes of the namespace to resolve foo (assuming that the table/view is actually dbo.foo): first under your default schema (john_doe.foo) and then, not being found, under dbo (dbo.foo'), whereas
select *
from dbo.foo
is immediately resolved with a single probe of the namespace.
This has 3 implications:
The redundant lookups are expensive.
It inhibits query plan caching, as every execution has to be re-evaluated, meaning the query has to be recompiled for every execution (and that takes out compile-time locks).
You will, at one point or another, shoot yourself in the foot, and inadvertently create something under your default schema that is supposed to exist (and perhaps already does) under the dbo schema. Now you've got two versions floating around.
At some point, you, or someone else (usually it happens in production) will run a query or execute a stored procedure and get...unexpected results. It will take you quite some time to figure out that there are two [differing] versions of the same object, and which one gets executed depends on their user credentials and whether or not the reference was schema-qualified.
Always schema-qualify unless you have a real reason not to.
That being said, it can sometimes be useful, for development purposes to be able to maintain the "new" version of something under your personal schema and the "current" version under the 'dbo' schema. It makes it easy to do side-by-side testing. However, it's not without risk (which see above).
When SQL sees the syntax it will first look at the current users schema to see if the table exists, and will use that one if it does.
If it doesn't then it looks at the dbo schema and uses the table from there
Related
I work at a company, 8 months, that provides software to about 80 customers. I have been informed by one of the owners that I am now also the DBA. Until I came here my SQL Server experience has been mostly in the ETL and BI realm, I do not mind learning, so I am diving in. Our base content, stored procedures, function, view etc. are stored in the dbo schema. When we create custom content for a customer this is placed in the cus schema. We do not use the schema designation when consuming content. I have been told that our customers access the database with a single user, access security is handled in the application, and this user has the default schema of cus. Our customers are on a mix of sql server editions from 2008 R2 to 2014 mostly standard or express.
My problem is this. If I make a change for a customer to the base code, dbo schema, and save it in the cus schema then I need to also create an object for everything that calls that object up the call stack in the cus schema otherwise it will not be called. When I was first told this I was like ok that does not make sense but if you say so. My thought being that if content was in the default schema, cus, it would be executed as SQL Server would look there first before looking in the dbo schema.
I now have the opportunity to create all of this content and decided to test my belief in how I thought SQL Server worked and found that what I believe is true but only sometimes. I hope this is not one of those it depends situations and I just do not understand. Anyway I created 7 very simple functions in the dbo and cus schema like this
CREATE FUNCTION [dbo].[Function_X](#Co int)
RETURNS TABLE
AS RETURN
(
Select 'cus' as sche, 'Function_X' as fun ,#co as passed
union all
Select * from Function_X_X(1)
)
They are identical except the ones in the dbo schema say dbo and the ones in the cus schema say cus. That way I know where they are called from. I actually tried to do this in extended events but could not find a way to do that, but I digress. The main function calls function_1, function_2, function_3 and function_4. Function_2 calls function_2_1 and that calls function_2_1_1 see below.
Function
Function_1
Function_2
Function_2_1
Function_2_1_1
Function_3
Function_4
By renaming various functions in the different schemas, so they would not be called, I was able to see which ones were executing in which schema. The results were not what I expected.
1 If the initial function is not in the cus schema then no functions in the cus schema are run.
2 Ignoring for the moment, functions 2_1 and 2_1_1, when the initial function is in the cus schema the four functions 1-4 will run from the cus schema if they exist otherwise they run from dbo so I will get a mix of cus and dbo schema depending on which functions exist.
3 If the function 2_1 is not found in cus then it runs in dbo as does function 2_1_1 even if it exists in the cus schema.
Example 2 executed as expected, however I was not expecting the results from example 1 or 3. I expected to see the cus function execute if it existed. In example 1 all the functions in the cus schema were ignored. In example 3 when function_2_1 was not found it ran the one in dbo as expected but then it also ran Function 2_1_1 from dbo.
Searching far and wide on the world wide web I could not see anything that touched on my question. I did stumble on a post that talked about the owner of the schema and the fact that they have a default schema. The owner dbo of the cus schema and the default schema of the login SA for the owner are both dbo. Thinking that might override the default schema, I created a new server login and database user with the default schema of cus and changed the owner of the cus schema. Alas no change in the result. I logged off the server and/or restarted the service when I did this. I did attempt this several times. I even created a new schema using the new login but got the same result.
Is there a way I can get SQL Server to use what is in the cus schema? Am I missing a setting or do I have to go and create all of that extra content?
The correct answer is to always fully qualify objects with the schema both when creating them and querying them. Relying on defaults is asking for trouble.
The response from, Erland Sommarkskog, located here:
https://social.msdn.microsoft.com/Forums/en-US/73678fa7-0b9c-4780-ae16-197bd30aba3a/i-have-a-default-schema-that-is-not-dbo-why-is-the-content-in-that-schema-not-always-consumed?forum=sqlsecurity
answered my question as to this behavior. "once you are inside a module, it is the module that determines the default schema of unqualified objects, never the default schema of the current user."
I am trying to build a dependency tree for a database by tracing object relationships through sys.sql_expression_dependencies (or I might use sys.dm_sql_referenced_entities instead if it's the only way to accomplish this).
I notice that for some referenced objects, I get the referenced_entity_name but not the referenced_schema_name or (crucially) the referenced_id. Looking through Microsoft's page on sys.sql_expression_dependencies I see that these columns can be NULL when "The schema of the referenced entity depends on the schema of the caller and is resolved at run time." Looking into the referencing stored procedure, I see that it refers to the referenced object as simply MyObject, not dbo.MyObject. (Virtually all of this database is in the dbo schema, so some people - myself included - have sometimes lazily neglected to include explicit schema references). I tried changing the procedure definition to explicitly reference dbo.MyObject, and then looked again in sys.sql_expression_dependencies, and I do indeed see the referenced_schema_name and the referenced_id that were previously missing.
I can't, at the moment, fix all objects in the database so that they make all references explicitly by schema. So, my question is: where the Microsoft page says "The schema of the referenced entity depends on the schema of the caller and is resolved at run time" does this simply mean that the SQL engine will always infer that the schema of the referenced object is the same as the schema of the referencing object? If so, I can then use MyReferencingObjectSchema + MyReferencedObjectSchema to look up the object_id for the referenced object and continue building my tree. Or is it more complicated than that?
It's more complicated than that, I'm afraid. It will look for an object with the right name in the connection's default schema as well. This is why you usually don't have to put the schema name in for dbo - that's the usual default schema.
What order it looks for things I don't know - default or current first - but that should be easily testable.
Following is my schema detail:
DB_A : schema_1, schema_2, schema_3
DB_B : schema_3
some procedures in schema_3 access resources(table, view, sp) from schema_1 and schema_2.
All procedures in schema_3 are same on both the dbs. How do I access schema_1 from schema_3 for both the dbs.
Now I can hard code DB_A in my procedures but when I move code to client machine, it will create a problem since DB_A may not be same(one of the reason being client is miser and having QA, Dev and Prod on same machine).
Second option is getting DB_A name as a parameter, but it will make all the schema_3 SPs dynamic (as I did not get any method to access something like #DBName.schema_name.ResourceName).
Third option is creating linked servers, which again do not solve my problem because of same reason as first.
Any idea how to proceed, where I do not want my procedures to be dynamic just because 80% of them are straight.
Edit Start:
So I can restate it as I have multiple databases with a database having resources (table/view/schema) which needs to be shared and then having other databases (one or more) which have stored procedures which computes on data from shared database and self database.
Shared database name is not going to be constant on all the environments and I want to change them(environment specific). I have come out with a solution where I will be creating synonym for all the shared resources and all procedures will be using them, that way they are all referring to shared resources from first database.
For each installation I need to modify synonyms definition to reflect correct share database name. Is there any SYNONYM For Database Name, that way I will have way less synonyms to handle.
Well the best choice I found is as follows.
Create Synonym (independent database DB_B) for individual objects (in shared database DB_A) with same name in same schema. That way your existing procedures need not change, and will work as required. Synonym gives a good reference on this. I will soon be creating an app to ease creating synonyms for these kind of situations.
CREATE SYNONYM DB_B.schema_1.proc_1 FOR DB_A.schema_1.proc_1
You can run your procedure in DB_A and create a view from DB_A to DB_B:
create view dbo.vw_B_Schema_3
as
select *
from DB_B.dbo.Schema_3
You'd have to create three versions of the view (dev, QA, prod.) But the view will be the only difference: procedure definitions can remain identical.
If DB_A and DB_B are on same server, only sure you that the login have permission in two database.
Now, use [database].[schema].[object], when you use object of others database
eg: I have two database, ("helpdesk", "intranet")
from heldesk to intranet
create view dbo.users
as
select login, name, lastname
from intranet.dbo.user // [database].[schema].[object] user is a table in dbo schema from intranet database.
where status = 1
;
I know databases aren't supported by CREATE SYNONYM, but I'm looking to achieve the functionality this would provide.
We've got Database A which contains views to tables on Database B. The trouble is "Database B" isn't always called "Database B". We use database projects for deployments, which at the moment fall over with an "Invalid Object Name" error if there isn't a "Database B".
The workaround at the moment is to open up the .dbschema file and do a find and replace. I guess another option would be to create a load of table synonyms.
What's the best way of changing the database a number of views reference without changing each view individually?
Thanks
Synonyms are a good way to do this. You have to create the synonyms at the object level though (as you've discovered). An easy way to do this would be to write a script that runs through the list of tables in DatabaseB (from your example) and creates a synonym for each one in DatabaseA. Keep the name of the synonym the same so the code in your views doesn't have to change. For instance, you you have tbl_a, tbl_b, and tbl_c in DatabaseB, you'd want your script to eventually do the following:
create synonym [otherDb].[tbl_a] for [DatabaseB].[schemaB].[tbl_a]
create synonym [otherDb].[tbl_b] for [DatabaseB].[schemaB].[tbl_b]
create synonym [otherDb].[tbl_c] for [DatabaseB].[schemaB].[tbl_c]
Now, in your view code, you'll always use [otherDb].[tbl_a], [otherDb].[tbl_b], and [otherDb].[tbl_c]. Hope this makes sense.
Last year I helped my current client with the implementation of a very similar design. We wrote a set of functions and stored procedures which generate the views automatically. Whenever you need to change the target database it generates the code to drop and recreate all of the views.
The code wasn't too difficult. It just uses the system tables to generate view code. I also wrote a Powershell prototype that uses SMO to do the same thing. The key is to have it automated to the point of requiring a single call so that you can do it easily and accurately.
We also included an exception table that used a pattern match of tables to exclude from view generation. It included a schema column and a table name column, each of which accepted LIKE patterns, so you could put "my_schema" and "%" to exclude all tables in the my_schema schema.
One master stored procedure accepted a target database name and would generate the entire script. Once the script is generated you can run it in SSMS or have that part automated as well.
This whole thing would be even easier if you just wanted to generate synonyms. We were using views so that we could change column lists, etc. and have the view DB look different than the target DB where needed.
What determines whether an Oracle object (table, view, etc.) is required to be qualified with a schema name (for example, schema.table_name, or schema.view_name, etc.)? At times I am able to access a remote objects (via a DB link) without having to qualify the schema, but other times, I receive an error stating that the "table or view doesn't exist", and to correct this, I must qualify the schema name.
I am aware that it is a best practice to always qualify a schema name, but I am just curious why I am able to access certain remote objects without a qualified schema, and others only with a qualified schema.
From the Oracle documentation:
http://download.oracle.com/docs/cd/B14117_01/server.101/b10759/sql_elements009.htm
The following example illustrates how Oracle resolves references to objects within SQL statements. Consider this statement that adds a row of data to a table identified by the name departments:
INSERT INTO departments
VALUES
(
280,
'ENTERTAINMENT_CLERK',
206,
1700);
Based on the context of the statement, Oracle determines that departments can be:
A table in your own schema
A view in your own schema
A private synonym for a table or view
A public synonym
Oracle always attempts to resolve an object reference within the namespaces in your own schema before considering namespaces outside your schema. In this example, Oracle attempts to resolve the name departments as follows:
First, Oracle attempts to locate the object in the namespace in your own schema containing tables, views, and private synonyms. If the object is a private synonym, then Oracle locates the object for which the synonym stands. This object could be in your own schema, another schema, or on another database. The object could also be another synonym, in which case Oracle locates the object for which this synonym stands.
If the object is in the namespace, then Oracle attempts to perform the statement on the object. In this example, Oracle attempts to add the row of data to departments. If the object is not of the correct type for the statement, then Oracle returns an error. In this example, departments must be a table, view, or a private synonym resolving to a table or view. If departments is a sequence, then Oracle returns an error.
If the object is not in any namespace searched in thus far, then Oracle searches the namespace containing public synonyms. If the object is in that namespace, then Oracle attempts to perform the statement on it. If the object is not of the correct type for the statement, then Oracle returns an error. In this example, if departments is a public synonym for a sequence, then Oracle returns an error.
What it's saying is that Oracle will check locally for objects you call before expanding its search outwards.
It may well be that there are public (or your own private) synonyms on some of your remote objects allowing you to reference them directly whereas those without the synonyms you'll have to fully qualify.
It depends on what username have you used when you logged in. Or what username was used when the database link was created/configured.
See, for each schema, there is a user. If you logged in as a user "XYZ", then you do not need to qualify object within the "XYZ" schema.