SQL Server and Oracle: Making the Connection

Linked servers can bring your SQL Server and Oracle databases together

Suppose you have a Microsoft .NET-enabled Internet ordering system but your warranty system is a legacy Oracle database application. You don't want to bill your commercial customers when they place orders for warranty replacement parts, so you need realtime answers from your Oracle database. Creating a linked server lets you use SQL Server to query your Oracle database in realtime to find out who your existing customers are.

When your data is distributed across several databases on multiple SQL Servers, linked servers let you run queries distributed across those servers. When all the servers are SQL Servers, configuration is easy, and SQL Server Books Online (BOL) covers everything you need to know. However, if some of your data is on an Oracle database, for example, configuring a linked server brings special challenges. You need to understand that even if you configure an Oracle linked server in SQL Server Enterprise Manager, your SQL Server is a client of your Oracle database. Therefore, you must successfully install and configure Oracle client software on your SQL Server. Oracle provides product support only for Oracle8 and later, so this article assumes that you're running Oracle8 or later. The Oracle Net8 library provides the client software that SQL Server needs.

In Oracle, a schema is the name for the entity SQL Server professionals know as a database. To connect to Oracle, you need to supply the schema name, password, and host string. An Oracle schema is owned by a specific Oracle username, so the schema name is also the username of the user who owns the schema. One Oracle username owns only one schema. You can find out more about the contents of a schema by querying the Oracle data dictionary, as I describe in the sidebar "System Procedures for Obtaining Linked-Server Metadata," page 26. The Oracle host string is also known as a service name or System Identifier (SID). What SQL Server calls an instance, Oracle calls a database. During an Oracle server installation, Oracle Universal Installer (a graphical interface similar to SQL Server's installation program) asks for a SID to use as the name of the Oracle database.

If you install your Oracle instance on a Windows machine and set your SID to Ora817, as in this article's example installation, you'll have a Windows 2000 service called OracleServiceORA817. This service is analogous to the MSSQLSERVER service for SQL Server 2000. For information about the architectural differences between the two products, see the Microsoft article "Migrating Oracle Databases to Microsoft SQL Server 7.0" (http://msdn.microsoft.com/library/techart/oracle2sql.htm).

Setting Up the Oracle Client on SQL Server


A SQL Server instance and its client exchange data through the application-level Tabular Data Stream (TDS) protocol by using a supported network protocol such as TCP/IP or IPX/SPX. Oracle servers and clients use Net8 to exchange data through three components: the Transparent Network Substrate (TNS), an Oracle protocol adapter, and a supported network library. Net8 uses a protocol adapter for translations between TNS and the network library. Net8, a replacement for SQL*Net, uses service names to find servers. The network libraries Net8 supports are TCP/IP, SPX, Named Pipes, Logical Unit Type 6.2 (LU6.2), and Bequeath. TCP/IP and SPX are the network libraries you can use to connect remote clients to Oracle servers. You use LU6.2 for compatibility with the IBM Advanced Program-to-Program Communications (APPC) architecture. Net8 uses the Bequeath network protocol for making connections when an Oracle client is logged in locally to the Oracle server.

For a Net8 client to make a successful connection to an Oracle server, the client must be able to find the service name for the server. Oracle clients can resolve service names by using a local client file called tnsnames.ora (which is analogous to a Windows HOSTS file), the DNS, or an Oracle Names Server. With Oracle9i, Lightweight Directory Access Protocol (LDAP) becomes another service-name resolution option. However, at press time, Microsoft didn't officially support Oracle9i as a linked server.

The Java-based Oracle Universal Installer helps you install Oracle server and client software on both Windows and UNIX systems. On a Windows system, inserting the Oracle installation CD-ROM autostarts Oracle Universal Installer. I recommend selecting a default installation of the Oracle8 client because in addition to installing Net8, the default installs the Net8 Assistant, the Net8 Configuration Assistant, and SQL*Plus and adds these options to your Start menu. SQL*Plus is a client tool comparable to SQL Server's osql.

Troubleshooting


Using SQL Server to validate a linked-server connection to Oracle can give misleading results. Until you execute a linked-server query, you can't be certain whether you configured the link correctly. Keep in mind that you can issue a linked-server query indirectly. In Enterprise Manager, if you select Tables or Views under your linked-server definition, you're indirectly querying the Oracle data dictionary. After I issued such a query, I received the error message in Figure 1. But when I executed Oracle's TNSPING command (similar to a TCP/IP PING, but specifically for testing connectivity to an Oracle database) from a command prompt, the results showed that Oracle client and networking components were installed, as Figure 2 shows.

Additionally, I had successfully used both the Net8 Assistant, as Figure 3 shows, and the Net8 Configuration Assistant to make actual connections to the example SCOTT schema, which is analogous to the SQL Server Pubs database. Using either of those network administration tools is sufficient for validating a database connection, so I knew that the problem lay elsewhere.

When you're creating linked servers to non-Microsoft databases, check the PATH environment variable. Note that Oracle incorrectly appears before SQL Server in the PATH variable that Figure 4 shows. To prevent the error message I received, edit PATH as Figure 5 shows, placing SQL Server ahead of any other vendor you're linking to. After I corrected PATH, the linked queries worked correctly.

Configuring SQL Server


Creating a linked Oracle server on SQL Server 2000 imposes an additional configuration requirement: You must manually add a key to the client machine's registry. (In this case, your SQL Server is the client of the Oracle server.) You need to choose the registry keys for the appropriate version of Windows and your Oracle server. You can find the appropriate registry files, which begin with mtx, in the C:\program files\common files\system\ole db folder. Double-click the appropriate registry file to add the necessary key. For more information, see BOL; go to Accessing and Changing Relational Data, Distributed Queries, OLE DB Providers Tested with SQL Server, OLE DB Provider for Oracle, then look under Registry Entries.

You create linked servers by executing a series of two system stored procedures. To begin, you use sp_addlinkedserver to specify your choice of OLE DB provider. The following example specifies Microsoft Data Access for Oracle:

sp_addlinkedserver 'OraDB', 'Oracle',
'MSDAORA', 'Ora817'

Or you can specify Oracle's OLE DB provider:

sp_addlinkedserver 'OraDB', 'Oracle',
'OraOLEDB.Oracle', 'Ora817'

You must pick one or the other of these options. Although Microsoft officially supports and recommends only the Microsoft OLE DB Provider for Oracle, this provider has limitations that I show later in an INSERT example. If you experience problems when you're using Microsoft's provider, try using Oracle's provider before calling Microsoft technical support.

The next step is to use sp_addlinkedsrvlogin to create a login to the linked server. SQL Server is like any other Oracle client in that it must supply a username and password to connect to the Oracle server. You can provide the username and password to sp_addlinkedsrvlogin in several different ways. In the first example that follows, I had already created a SQL Server login of sys with a password of change_on_install to let SQL Server impersonate an Oracle username and password by supplying a SQL Server username and password to Oracle. By default, the Oracle user sys has a password of change_on_install. Because the usernames and passwords for the two database servers match, the SQL Server credentials can impersonate the Oracle credentials. No mapping of SQL Server credentials is required in this case, so I used true as the value of the second parameter, indicating that impersonation of credentials will take place. In the second example, the Win2K user Administrator is mapped to the Oracle user scott with a password of tiger. The last example directs all other SQL Server logins to connect as scott with a password of tiger. The latter two examples pass specific credentials; because they don't use impersonation, the second parameter is false.

sp_addlinkedsrvlogin 'OraDB', true, 'sys'
sp_addlinkedsrvlogin 'OraDB', false,
   'W2000AS\Administrator', 'scott', 'tiger'
sp_addlinkedsrvlogin 'OraDB', false, NULL, 'scott',
'tiger'

Figure 6, page 28, shows the security settings on the Linked Server Properties dialog box's Security tab. You can access this dialog box from Enterprise Manager by opening Linked Servers in the Security folder.

Executing an Oracle Query from SQL Server


You can query the SCOTT schema by using Oracle's SQL*Plus or a third-party freeware tool such as Quest Software's TOAD, which Figure 7, page 28, shows. (TOAD is available from http://www.toadsoft.com.) Oracle tools are crude by Microsoft standards: SQL*Plus has functionality similar to the osql command-line tool. TOAD, however, is comparable to SQL Server 2000 Query Analyzer and Object Browser. To start SQL*Plus, choose Start, Programs, Oracle, Application Development, SQL*Plus. In the Log On dialog box that Figure 8, page 28, shows, use tiger as the password. Note that all entries are case insensitive. At the SQL> prompt, enter the following query:

SELECT * FROM dept WHERE deptno = 10;

You'll get the following output:

    DEPTNO DNAME          LOC
       
---------- -------------- -------------

        10 ACCOUNTING     NEW YORK

Now, try executing the above SELECT statement as a SQL Server linked-server query from Query Analyzer. Although this query requires a fully qualified four-part name for resolution, the catalog is always left empty because all Oracle metadata resides in one centralized system catalog called SYSTEM.

SELECT * FROM oradb..scott.dept WHERE deptno = 10

Executing the above query returns the following error:

<i>Server: Msg 7314, Level 16, State 1, Line 1
OLE DB provider 'oradb' does not contain table '"scott"."dept"'.
The table either does not exist or the current user does not have
permissions on that table.</i>

You get this error because the Oracle data dictionary is case sensitive. Oracle object names are all uppercase by default. The linked-server query must exactly match the case of all referenced Oracle metadata, such as schema and table names. So, the following query works:

SELECT * FROM oradb..SCOTT.DEPT WHERE deptno = 10

To prevent similar errors, you can create a view to give a case-insensitive representation of a linked-server table. You begin by creating a SQL Server view of an Oracle table:

CREATE VIEW dbo.v_oradb_scott_dept
AS
SELECT *
FROM oradb..SCOTT.DEPT

By querying this view, you can see that the case sensitivity of Oracle metadata no longer affects SQL Server queries against the data. This convenience comes at a price, however. SQL Server resolves the query's WHERE clause locally. Because Oracle doesn't see the WHERE clause, it does a full table scan and sends the entire table across the network.

SQL Server 2000 offers an alternative to creating a view. Inline table-valued user-defined functions (UDFs) can give you the same functionality as a parameterized view. You can query UDFs as you would a view, but UDFs can accept parameters, which a view can't. You can create the following UDF:

CREATE FUNCTION fn_oradb_scott_dept (@deptno int)
RETURNS TABLE
AS RETURN (SELECT * FROM oradb..SCOTT.DEPT WHERE deptno
  = @deptno)

When you execute this function instead of querying the view, as follows, the WHERE clause is resolved remotely on the Oracle server:

SELECT * FROM fn_oradb_scott_dept(10)

This approach gives you better performance and reduced network traffic compared with using a view.

Updating Linked Data


Now, consider executing a simple INSERT statement from SQL*Plus. Note that unlike SQL Server, Oracle requires the INSERT INTO syntax in every INSERT statement:

INSERT INTO dept (deptno) values (1);

If you execute a similar query from Query Analyzer, as follows,

INSERT INTO oradb..SCOTT.DEPT (deptno) values (2)

you get the following error message:

<i>Server: Msg 7344, Level 16, State 1, Line 1</i>
<i>OLE DB provider 'MSDAORA' could not INSERT INTO table
'\[oradb\]..\[SCOTT\].\[DEPT\]' because of column 'DNAME'. The
column used the default value.</i>
<i>\[OLE/DB provider returned message: Multiple-step operation
generated errors. Check each status value.\]</i>

This example illustrates a limitation of the Microsoft OLE DB provider. The DEPT table has three columns: deptno, dname, and loc. The dname and loc columns are nullable, so they shouldn't be necessary in an INSERT statement. The Microsoft provider doesn't properly accommodate nullable columns if they're not supplied in an INSERT statement, but you can use a simple workaround. Specify the nullable columns and give them values:

INSERT oradb..SCOTT.DEPT (deptno,dname,loc) values (2,NULL,NULL)

Also note that when you're inserting data into an Oracle table from SQL Server, you don't need to include INTO. Both the Microsoft and Oracle OLE DB providers add INTO to the INSERT statement they execute on the Oracle server.

Using the Oracle Provider for OLE DB, I created a new linked server named orclprvdr and attempted a linked-server INSERT, as follows:

INSERT orclprvdr..SCOTT.DEPT (deptno) values (3)

This query worked, so you can see that Oracle's provider offers advantages when you're doing linked-query INSERT statements. However, Oracle's provider can't execute an INSERT into a table that contains a column that has a DEFAULT constraint defined on it. Microsoft's provider can execute that INSERT as long as you pass an actual value for the column instead of using the keyword DEFAULT.

Unlike an INSERT statement, an UPDATE statement doesn't have problems with nullable columns. If you're issuing a linked-server UPDATE to Oracle, you don't need to specify the nullable columns if you aren't updating them.

UPDATE oradb..SCOTT.DEPT set deptno = 3 WHERE deptno = 2

If you need to join a SQL Server table to an Oracle table, you're executing a heterogeneous query and you must have a linked server. To construct the following example, I used Data Transformation Services (DTS) to move a copy of the Northwind database's Orders table to the SCOTT schema, changing the default destination of "SCOTT"."Orders" to "SCOTT"."ORDERS". I changed the case here to avoid having to use quoted identifiers when I'm using Oracle tools to refer to the table. Oracle databases can support mixed-case object names, but administration and development of an Oracle database is easier when you use all-uppercase object names. The following query joins the Northwind Employees table to the SCOTT schema's copy of Orders:

SELECT lastname FROM employees e
INNER JOIN oradb..SCOTT.ORDERS o
ON e.employeeid = o.employeeid
WHERE o.orderid = 10248

If your heterogeneous query needs to be inside a transaction, you must have the Microsoft Distributed Transaction Coordinator (MS DTC) service running on your SQL Server. For more information, see the sidebar "Transactions," page 28.

Here's one more way to execute a query when you're using a linked-server definition. SQL Server's OPENQUERY system-defined function passes the query through from SQL Server to Oracle:

SELECT * FROM OPENQUERY (oradb,'SELECT * FROM dept
WHERE deptno = 10')

Using OPENQUERY doesn't incur the overhead of the Oracle metadata lookups that I discuss in the sidebar "Analyzing Linked-Server Queries," page 30. With OPENQUERY, all query processing happens on the Oracle linked server and you avoid the metadata lookups. OPENQUERY performs a linked-server query better, but you can't use it in a heterogeneous query.

OPENROWSET is a pass-through query that doesn't use a linked-server definition. It's similar to OPENQUERY, except that it requires you to provide all connection criteria as input parameters:

SELECT * FROM OPENROWSET ('MSDAORA','Ora817';
'SCOTT';'TIGER',' SELECT * FROM dept
WHERE deptno = 10')

Linked servers are only for executing queries, but as the sidebar "Executing Stored Procedures Remotely" shows, you can execute a query to execute a stored procedure.

Bear in Mind


Before you can use your linked-server setup effectively, you need to know the differences between Oracle and SQL Server data types. If you're storing historical dates in both Oracle and SQL Server, be aware that Oracle's date data type can store dates back to January 1, 4712 b.c., but SQL Server's datetime data type begins at a.d. January 1, 1753. Additionally, Oracle's date data type has a granularity of 0.01 second and SQL Server's datetime has a granularity of 0.001 second. (For more information about the SQL Server datetime data type, see Kalen Delaney, Inside SQL Server, "Solving the Datetime Mystery," September 2000.) For more information about Oracle data types, go to http://technet.oracle.com. You'll need to register (it's free) to get access to the Oracle documentation.

Sometimes you need to get data from more than one database or even from another database server. And when the other database is Oracle, that task is particularly challenging. Microsoft isn't in the business of providing customer support for Oracle databases, and Oracle isn't in the business of providing customer support for SQL Server. However, linked servers can provide convenient access to Oracle data from SQL Server. Now that you know how to configure and use Oracle linked servers, you can get realtime Oracle data in the SQL Server environment you're comfortable in.

Discuss this Article 18

Anonymous User (not verified)
on Nov 17, 2004
Outstanding article, I've linked multiple SQL Servers before but this time I was able to link to an Oracle 10g database -- good job!
Anonymous User (not verified)
on Aug 10, 2005
Thanks article great. Also last user that posted set "Allow InProcess", worked prefect after recreating thru Enterprise with this option. Thanks
alex (not verified)
on Jun 17, 2004
Nice article, but as I am from Germany I have no clue where to find the PATH environment variable described above. Would be nice to get help, because I think this is where my problems comes from.
Richard M. Bagdonas (not verified)
on Nov 28, 2001
What about wrapping a distributed transaction around multiple inserts into Oracle and an update into SQL? The MSDAORO OLEDB Provider seems to hurl upon initiating a distributed transaction around the whole lot. Any ideas?
Aleksandar Jovanovic (not verified)
on Jun 12, 2004
excellent
Jason Nunn (not verified)
on Nov 12, 2001
I liked you article. I have been working with SQL Server and Oracle linked in a production environment for 2 years now. You brought up the interesting use of a UDF for selecting from Oracle. I find that best performance for Inserts is achieved by the construct of : INSERT INTO OPENQUERY(ORACLEDB, 'Select ... FROM MYTABLE WHERE 1=2') VALUES ( ... ) What do you think ?
Anonymous User (not verified)
on Nov 22, 2004
In SQL Server I create view like "select * from..." source is from Oracle Table. When I update oracle table struture (like add a column) SQL Server (via Linked Server) doesn't update schema...of course, I try refresh, but no effect... Somebody have an idea ? :) Thanks
Anonymous User (not verified)
on Jan 26, 2005
Anonymous User above, Any query over a network with joins will take ages. If you join two tables, the number of "reads" is very roughly equal to the total number of rows multiplied together I think. Hence all these reads become network reads so it takes ages. Try creating a view in oracle and querying that instead - that should be quicker. Also if you want to get technical check the join order of the query plan. Anyway my comments - article is good, however I still can't get it to work. Having changed the path variable does anyone know if I have to restart the SQL server? Cheers Will
Anonymous User (not verified)
on Jun 12, 2005
- SQL Server 2000 & Oracle 10g - Oracle10g Provider for OLE DB Version 10.1.0.4.0 Able to create linked server and view all tables. However, query give Server: Msg 7320, Level 16, State 2, Line 1 Could not execute query against OLE DB provider 'OraOLEDB.Oracle'. OLE DB error trace [OLE/DB Provider 'OraOLEDB.Oracle' ICommandText::Execute returned 0x80040155]. Query is select count(DEPT_CODE) from ORADB..ORADBA.DEPARTMENTS Have changed the PATH. But still the same error. What other config did I miss out ? Pls help... Txs.
Anonymous User (not verified)
on Mar 4, 2005
Very nice article. I established successfully connection to Oracle but I am unable to execute Stored Procedure. Any clue why?
Miguel Angel (not verified)
on Mar 30, 2005
I have: Oracle 8i and SQL Server 2000. I want to create a link server with a Oracle Database. First: I try with the OLE DB Provider of Oracle. I think that all the necesary data is ok, because I can see all the tables of the Oracle Database in my SQL Server and I can create views of the tables, but when I do a select of a table with more of 100 register I obtain an error of the controler like: Servidor: mensaje 7399, nivel 16, estado 1, línea 1 El proveedor OLE DB 'OraOLEDB.Oracle' informa de un error. El proveedor no proporcionó información acerca del error. Second: I try with the OLEDB Provider of Microsoft and I can to create the linked server, but whe I try to create a views of any table of the Oracle Data Base I obtain an error like: Servidor: mensaje 7399, nivel 16, estado 1, línea 1 El proveedor OLE DB 'MSDAORA' informa de un error. El proveedor no proporcionó información acerca del error. I execute the register script for my windows 2000 correctly but I obtain this error too. What is the problem with the two OLE DB Provider? Thanks
Anonymous User (not verified)
on Sep 2, 2005
Hi, I have setup a linked server that intermittently crashes. It connects to an Oracle Database on an alternate server. My tnsnames.ora file is correct and the Linked server does periodically work.... I am not sure what triggers it but it crashes and gives the following error: Error 7399: OLE DB provider 'MSDAORA' reported an error. The provider did not give any information about the error. OLE DB error trace [OLE/DB Provider 'MSDAORA' IDBInitialize::Initialize returned 0x80004005: The provider did not give any information about the error.]. Can anyone help with this please ? Which is the correct configuration in terms of drivers to use and vendor. Are there any registry settings that need changing? Thanks, Simon.
Anonymous User (not verified)
on Jun 21, 2005
Answer for the last comment. I had the same prob.This one solved it: Message 3 Error 7302: Could not create an instance of OLE DB provider 'MSDAORA' Make sure that the MSDAORA.dll file is registered correctly. (The MSDAORA.dll file is the Microsoft OLE DB provider for Oracle file.) Use RegSvr32.exe to register Microsoft OLE DB Provider for Oracle. If the registration fails, reinstall Microsoft Data Access Components (MDAC). For more information about MDAC, visit the following Microsoft Developer Network (MSDN) Web site: http://msdn.microsoft.com/data/Default.aspx Note If you use a third-party Oracle provider, and your Oracle provider cannot run outside a SQL Server process, enable it to run in-process by changing the provider options. To change the provider options, use one of the following methods. • Method 1 Locate the following registry key. Then, change the value of the AllowInProcess (DWORD) entry to 1. This registry key is located under the corresponding provider name: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Providers\ProviderName • Method 2 Set the Allow InProcess option directly through SQL Server Enterprise Manager when you add a new linked server. Click Provider Options, and then click to select the Allow InProcess check box.
GARY (not verified)
on Sep 13, 2004
I agree with Samir.. The advice on the PATH variable was just what the doctor ordered.. Thanks again!!
Anonymous User (not verified)
on Jun 21, 2005
Another comment about last post: i deleted the linked server and add it from begining but this time i did this: Method 2 Set the Allow InProcess option directly through SQL Server Enterprise Manager when you add a new linked server. Click Provider Options, and then click to select the Allow InProcess check box.
Anonymous User (not verified)
on Jan 6, 2005
Hi : I am retriving data from a oracle database using sql server linked server. I found that when I run the query in sql plus (oracle) the query executes within seconds but when I am executing the same query using sql server query analyzer it is taking more than 10 min. What could be the problem. my query involves inner join on 4 tables with millions of data and distinct and where clause. I get 6 rows with the query I am using.
Samir Shah (not verified)
on Jul 2, 2004
Fantastic!!! Truly useful. Saved a lot of time and frustration. the details on change in PATH file did the trick. Thanks a lot.
Patrick (not verified)
on Nov 17, 2005
Great article, this was exactly what I needed. But, having made the connection work, I found a new problem: I can query an Oracle table, but if I want to use SELECT * FROM table, it's not always working. As soon as the number of fields exceeds eight (8), the query times out. If I use the same query, but with one field less in the SELECT statement, is gives me back the result in a second. It doesn't matter which of the field I put in or leave out. Example: This works (8 fields): SELECT MEMBER_ID, GIVEN_NAME, FAMILY_NAME, NAME_PREFIX, EMAIL, STREET, TOWN, POSTCODE FROM dbora..USERNAME.MEMBER_DATA This doesn't (9 fields): SELECT MEMBER_ID, GIVEN_NAME, FAMILY_NAME, NAME_PREFIX, EMAIL, STREET, TOWN, POSTCODE, COUNTRY FROM dbora..USERNAME.MEMBER_DATA Obviously, SELECT * FROM dbora..USERNAME.MEMBER_DATA doesn't work either, since it has more fields than 8. I get the same results using OPENQUERY. In the table are currently only 11 records, so it assume it's not the size of the table. I'm using SQL Server 2000 on Windows Server 2003, linking to an Oracle 9 database. Does anyone have any idea why this occurs? Thanks, Patrick

Please or Register to post comments.

IT/Dev Connections

Las Vegas
September 30th - October 4th

Paul ThurottOur Experts will show you:
• Common SQL Server
Problems
• Best Practices for T-SQL
• SQL Server Integration
Services
• Database Development

Come See Michael Otey & Tim Ford in Person!

Early Registration Now Open

From the Blogs
May 21, 2013
blog

A Common Misconception about MAXDOP

Out of the box, SQL Server is (and has been) able to take advantage of multiple processors/cores without any effort on behalf of administrators....More
May 9, 2013
blog

My ISO 8601-Compliant Signature 2

My family recently just "officially" announced that we're in the process of adopting a child from South Africa. We're quite excited, of course, but there's a ton of paperwork to do—along with the need for gobs of signatures....More
May 8, 2013
blog

Use SSIS for ETL from Hadoop

In this blog post, Mark Kromer walks you through using SSIS as a way to use ETL techniques using Microsoft's Hadoop on Windows (HDInsight) as a source using Hive connectors...More
SQL Server Pro Forums

Get answers to questions, share tips, and engage with the SQL Server community in our Forums.