SQL Server Object Names - sql

I am wondering if someone can explain the concept of uniquely identifying sql server objects in a join.
In my example there are 2 schemas and 2 tables (but with same name). My assumption was that even though table name might be same between 2 schemas, as long as they are referenced with their full qualified name databasename.schemaname.objectname, SQL server should be able to make out the difference. However that does not seem to be the case and the workaround for this is to use alias.
I would appreciate If someone can explain or point out to some literature around why sql server cannot uniquely identity these.
CREATE SCHEMA [Sch1]
GO
CREATE SCHEMA [Sch2]
GO
CREATE TABLE [Sch1].[Table_1](
[ID] [int] NULL,
[DESC] [nchar](10) NULL
) ON [PRIMARY]
GO
CREATE TABLE [Sch2].[Table_1](
[ID] [int] NULL,
[DESC] [nchar](10) NULL
) ON [PRIMARY]
GO
Select *
From Sch1.Table_1
Join Sch2.Table_1
on Sch1.Table_1.Id = Sch2.Table_1.Id

The SQL Server supports muliti-part identifiers:
linked_server.db_name.schema.table_name
In your case you have:
Select *
From Sch1.Table_1
Join Sch2.Table_1
on Sch1.Table_1.Id = Sch2.Table_1.Id
Now you wonder why SQL Server cannot differentiate between them:
Sch1.Table_1 != Sch2.Table_1
The case is because of SQL Server use something called exposed name.
exposed name
which is the last part of the multi-part table name (if there is no
alias), or alias name when present
Returning to your query you have exposed names Table_1 and Table_1 which are duplicates and you need to use aliases.
From SQL Server 2005+:
Duplicate table detection algorithm has been changed correspondingly,
so that any tables with the same exposed names will be considered
duplicates
I suspect that your code could work with SQL Server 2000 but I cannot check it for sure.
For more info read Msg 1013

As far as I can tell, I don't see any errors in your sample code. Please explain in detail what errors you're encountering.
As for the four-part naming convention. the full object name syntax is:
server.database.schema.object
So a complete usage would be, for example:
select * from servername.databasename.Sch1.Table_1
or
select * from servername.databasename.Sch2.Table_2
from which you can ignore any part as long as there is no ambiguity. Therefore in your example you can ignore severname and databasename as they are the same. But you cannot ignore schema names as they are not.
Addendum:
Based on error message you posted later, you need to employ correlation naming on the join syntax:
select *
from Sch1.Table_1 as t1
inner join Sch2.Table_1 as t2 on t1.ID=t2.ID

Select *
From Sch1.Table_1 x
Join Sch2.Table_1 y
on x.Id = y.Id
Does this work?

Related

Alias naming the same table used to self compare inside query (ORACLE DB)

I have a clients table and I'm writing a query to see which clients share the same phone number.
The structure and the query :
CREATE TABLE Client
(noClient INTEGER NOT NULL,
nameClient VARCHAR(20) NOT NULL,
noTelephone VARCHAR(15) NOT NULL,
PRIMARY KEY (noClient)
);
SELECT c.nameClient, c2.nameClient2
FROM Client c NATURAL JOIN Client c2(noClient2, nameClient2, noTelephone)
WHERE c.nomClient < c2.nomClient
The problem here is I keep getting an error
FROM Client c NATURAL JOIN Client c2(noClient2, nameClient2, noTelephone)
*
ERROR at line 2:
ORA-00933: SQL Command not properly ended
Is there a proper way to rename the table AND its columns?
I have a solution around that but i'd like to know the proper syntax of making an alias of a table (and it's columns) inside the query.
And also of course if there are better ways to approach when using the same table to compare it's values
The workaround is to force the join on the noTelephone without renaming the columns :
SELECT c.nameClient, c2.nameClient
FROM Client c JOIN Client c2 ON c.noTelephone = c2.noTelephone
WHERE c.nameClient < c2.nameClient
If your tables doesn't have foreign key constraints the NATURAL JOIN clause won't work.
BTW please avoid using NATURAL JOIN, use explicit JOIN has explained here:
https://rules.sonarsource.com/plsql/RSPEC-2521

Shorten the table names

I am using SQL Server and wondering if it's possible to shorten the table names. For example, my current SQL syntax is:
SELECT
t.Titlename
FROM [dbo].[Order] o
JOIN [dbo].[Title] t ON o.titleid=t.TitleId
However, if I just try doing select titlename from order I get an error. Why is this so? What would be the 'shortest' tablename I could reference; and why is it requiring the [dbo] namespace?
It doesn't require the [dbo] namespace, assuming that is the currently selected database. But it does require the Title table to be joined to Order to get the Titlename column:
SELECT
t.Titlename
FROM [Order] o
JOIN Title t ON o.titleid=t.TitleId
Demo on SQLFiddle
Note that Order must be enclosed in [] because it is an SQL reserved keyword.
It's also worth noting the point that is made in the article #GregLow mentions, which indicates that queries cannot be reused if object name resolution needs to occur. So it would generally be preferred to write the query as you originally did.
If your currently selected database be dbo, then it shouldn't be necessary to use the fully qualified object name. However, you have another problem, because ORDER is a reserved SQL Server keyword. The following should work:
SELECT t.Titlename
FROM [Order] o
INNER JOIN [Title] t
ON o.titleid = t.TitleId;
Here, using [Order] instead of Order counts as escaping that table name.

Difference between DELETE and DELETE FROM in SQL?

Is there one? I am researching some stored procedures, and in one place I found the following line:
DELETE BI_Appointments
WHERE VisitType != (
SELECT TOP 1 CheckupType
FROM BI_Settings
WHERE DoctorName = #DoctorName)
Would that do the same thing as:
DELETE FROM BI_Appointments
WHERE VisitType != (
SELECT TOP 1 CheckupType
FROM BI_Settings
WHERE DoctorName = #DoctorName)
Or is it a syntax error, or something entirely different?
Assuming this is T-SQL or MS SQL Server, there is no difference and the statements are identical. The first FROM keyword is syntactically optional in a DELETE statement.
http://technet.microsoft.com/en-us/library/ms189835.aspx
The keyword is optional for two reasons.
First, the standard requires the FROM keyword in the clause, so it would have to be there for standards compliance.
Second, although the keyword is redundant, that's probably not why it's optional. I believe that it's because SQL Server allows you to specify a JOIN in the DELETE statement, and making the first FROM mandatory makes it awkward.
For example, here's a normal delete:
DELETE FROM Employee WHERE ID = #value
And that can be shortened to:
DELETE Employee WHERE ID = #value
And SQL Server allows you to delete based on another table with a JOIN:
DELETE Employee
FROM Employee
JOIN Site
ON Employee.SiteID = Site.ID
WHERE Site.Status = 'Closed'
If the first FROM keyword were not optional, the query above would need to look like this:
DELETE FROM Employee
FROM Employee
JOIN Site
ON Employee.SiteID = Site.ID
WHERE Site.Status = 'Closed'
This above query is perfectly valid and does execute, but it's a very awkward query to read. It's hard to tell that it's a single query. It looks like two got mashed together because of the "duplicate" FROM clauses.
Side note: Your example subqueries are potentially non-deterministic since there is no ORDER BY clause.
Hi friends there is no difference between delete and delete from in oracle database it is optional, but this is standard to write code like this
DELETE FROM table [ WHERE condition ]
this is sql-92 standard. always develop your code in the standard way.

How do I perform update query with subquery in Access?

I want to port this SQL query, which works just fine on SQL Server, MySQL, and Oracle, to an Access database. How do I do that? Right now it prompts me for a Company_ID for some reason.
Edit: I was getting the prompt because I forgot to first create the Company_ID column in VendorRegKeys. Now I am getting error "Operation must use an updateable query".
UPDATE VendorRegKeys
SET Company_ID = (SELECT Users.Company_ID
FROM Users
WHERE Users.User_ID = VendorRegKeys.CreatedBy_ID)
Update: I found this to work based on JuniorFlip's answer:
UPDATE VendorRegKeys, Users
SET VendorRegKeys.Company_ID = Users.Company_ID
WHERE VendorRegKeys.CreatedBy_ID = Users.User_ID
Straight answer: you can't. The Access Database Engine simple does not support the vanilla SQL-92 scalar subquery syntax even when in its own so-called ANSI-92 Query Mode.
You are forced to use its own proprietary syntax which does not enforce the scalar requirement i.e. is unsafe and will pick a value arbitrarily and silently**. Further, beyond simple constructs it does not work at all, most notably where your subquery (if you were allowed to use one in the first place) uses an set function (MAX, SUM, etc) -- see this article for some really unsatisfactory workarounds.
Sorry to be negative but this is really basic syntax and I can't understand why the Access team haven't gotten around to fixing it yet. It is the undisputed number one reason why I can't take the Access database engine seriously anymore.
To demonstrate the unsafe behavior of the Access proprietary UPDATE..JOIN..Set syntax
CREATE TABLE Users
(
User_ID CHAR( 3 ) NOT NULL,
Company_ID CHAR( 4 ) NOT NULL,
UNIQUE ( Company_ID, User_ID ) );
CREATE TABLE VendorRegKeys
CreatedBy_ID CHAR( 3 ) NOT NULL UNIQUE,
Company_ID CHAR( 4 ) );
INSERT INTO Users VALUES ( 'Kip', 'MSFT' );
INSERT INTO Users VALUES ( 'Kip', 'AAPL' );
INSERT INTO VendorRegKeys VALUES ( 'Kip', NULL );
UPDATE VendorRegKeys
INNER JOIN Users ON Users.User_ID = VendorRegKeys.CreatedBy_ID
SET VendorRegKeys.Company_ID = Users.Company_ID;
When executing the update statement within Access, the UI warns we
You are about to update 2 row(s).
despite the fact there is only one row in the VendorRegKeys table!
What happens in practise is just one of the values we will used to update the column in that single row, without a reliable way of predicting which it will be.
With Standard SQL's scalar subquery syntax, you would get an error and the statement would fail to execute, which is arguably the desired functionality (Standard SQL's MERGE syntax behaves this way too).
That could be because Company_ID is not an existing field in VendorRegKeys OR Users.
EDIT:
UPDATE VendorRegKeys
INNER JOIN Users ON Users.User_ID = VendorRegKeys.CreatedBy_ID
SET VendorRegKeys.Company_ID = Users.Company_ID
you could try this one
update a
set a.company_id = b.company_id
from vendorRegkeys a, users b
where a.createdby_id = b.user_id

SQL - table alias scope

I've just learned ( yesterday ) to use "exists" instead of "in".
BAD
select * from table where nameid in (
select nameid from othertable where otherdesc = 'SomeDesc' )
GOOD
select * from table t where exists (
select nameid from othertable o where t.nameid = o.nameid and otherdesc = 'SomeDesc' )
And I have some questions about this:
1) The explanation as I understood was: "The reason why this is better is because only the matching values will be returned instead of building a massive list of possible results". Does that mean that while the first subquery might return 900 results the second will return only 1 ( yes or no )?
2) In the past I have had the RDBMS complainin: "only the first 1000 rows might be retrieved", this second approach would solve that problem?
3) What is the scope of the alias in the second subquery?... does the alias only lives in the parenthesis?
for example
select * from table t where exists (
select nameid from othertable o where t.nameid = o.nameid and otherdesc = 'SomeDesc' )
AND
select nameid from othertable o where t.nameid = o.nameid and otherdesc = 'SomeOtherDesc' )
That is, if I use the same alias ( o for table othertable ) In the second "exist" will it present any problem with the first exists? or are they totally independent?
Is this something Oracle only related or it is valid for most RDBMS?
Thanks a lot
It's specific to each DBMS and depends on the query optimizer. Some optimizers detect IN clause and translate it.
In all DBMSes I tested, alias is only valid inside the ( )
BTW, you can rewrite the query as:
select t.*
from table t
join othertable o on t.nameid = o.nameid
and o.otherdesc in ('SomeDesc','SomeOtherDesc');
And, to answer your questions:
Yes
Yes
Yes
You are treading into complicated territory, known as 'correlated sub-queries'. Since we don't have detailed information about your tables and the key structures, some of the answers can only be 'maybe'.
In your initial IN query, the notation would be valid whether or not OtherTable contains a column NameID (and, indeed, whether OtherDesc exists as a column in Table or OtherTable - which is not clear in any of your examples, but presumably is a column of OtherTable). This behaviour is what makes a correlated sub-query into a correlated sub-query. It is also a routine source of angst for people when they first run into it - invariably by accident. Since the SQL standard mandates the behaviour of interpreting a name in the sub-query as referring to a column in the outer query if there is no column with the relevant name in the tables mentioned in the sub-query but there is a column with the relevant name in the tables mentioned in the outer (main) query, no product that wants to claim conformance to (this bit of) the SQL standard will do anything different.
The answer to your Q1 is "it depends", but given plausible assumptions (NameID exists as a column in both tables; OtherDesc only exists in OtherTable), the results should be the same in terms of the data set returned, but may not be equivalent in terms of performance.
The answer to your Q2 is that in the past, you were using an inferior if not defective DBMS. If it supported EXISTS, then the DBMS might still complain about the cardinality of the result.
The answer to your Q3 as applied to the first EXISTS query is "t is available as an alias throughout the statement, but o is only available as an alias inside the parentheses". As applied to your second example box - with AND connecting two sub-selects (the second of which is missing the open parenthesis when I'm looking at it), then "t is available as an alias throughout the statement and refers to the same table, but there are two different aliases both labelled 'o', one for each sub-query". Note that the query might return no data if OtherDesc is unique for a given NameID value in OtherTable; otherwise, it requires two rows in OtherTable with the same NameID and the two OtherDesc values for each row in Table with that NameID value.
Oracle-specific: When you write a query using the IN clause, you're telling the rule-based optimizer that you want the inner query to drive the outer query. When you write EXISTS in a where clause, you're telling the optimizer that you want the outer query to be run first, using each value to fetch a value from the inner query. See "Difference between IN and EXISTS in subqueries".
Probably.
Alias declared inside subquery lives inside subquery. By the way, I don't think your example with 2 ANDed subqueries is valid SQL. Did you mean UNION instead of AND?
Personally I would use a join, rather than a subquery for this.
SELECT t.*
FROM yourTable t
INNER JOIN otherTable ot
ON (t.nameid = ot.nameid AND ot.otherdesc = 'SomeDesc')
It is difficult to generalize that EXISTS is always better than IN. Logically if that is the case, then SQL community would have replaced IN with EXISTS...
Also, please note that IN and EXISTS are not same, the results may be different when you use the two...
With IN, usually its a Full Table Scan of the inner table once without removing NULLs (so if you have NULLs in your inner table, IN will not remove NULLS by default)... While EXISTS removes NULL and in case of correlated subquery, it runs inner query for every row from outer query.
Assuming there are no NULLS and its a simple query (with no correlation), EXIST might perform better if the row you are finding is not the last row. If it happens to be the last row, EXISTS may need to scan till the end like IN.. so similar performance...
But IN and EXISTS are not interchangeable...