martes, 5 de noviembre de 2013

Creando un Servidor Vinculado de SQL Server a SQLite para Importar Datos

Problema

En la actualidad utilizamos y desarrollamos un sinnúmero de aplicaciones basadas en tecnologías móviles y el motor de base de datos por excelencia para este tipo de aplicaciones es SQLite, líder indiscutible en este segmento de tecnológico. En ocasiones nos veremos obligados a importar datos desde SQLite hacia SQL Server, y este artículo tiene como finalidad mostrar una guía paso a paso de cómo realizar esta tarea que ha cobrado tanta importancia en estos momentos donde el auge de las aplicaciones para dispositivos móviles crece a un ritmo muy acelerado.

Solución

Existen varias formas alternativas de como importar los datos desde SQLite hacia SQL Server, nosotros como solución implementaremos un Linked Server hacia SQLite.

Dividiremos esta solución en los siguientes pasos:

  1. Descargar un controlador ODBC para SQLite
  2. Instalar el controlador
  3. Crear un DSN a nivel de sistema para la base de datos en SQLite
  4. Crear el Linked Server en SQL Server
  5. Seleccionar los datos de la fuente e insertarlos en nuestra tabla en SQL Server

  1. Descargar un controlador ODBC para SQLite
  2. Ir a esta página donde se encuentra el controlador ODBC para SQLite. Configurar el controlador correcto es algunas veces la parte más difícil, por lo que recomendamos descargar ambos controladores tanto el de 32 como el de 64 bits.

  3. Instalar el controlador
  4. Elija el controlador que le corresponda dependiendo de si su sistema operativo es de 32 o 64 bits y ejecute el archivo ejecutable (.exe) correspondiente.















  5. Crear un DSN a nivel de sistema para la base de datos en SQLite

  6. Presione Inicio -> Ejecutar y luego digite odbcad32 y presione retorno (enter) para el administrador odbc de 64 bits.


    Presione Inicio -> Ejecutar y luego digite C:\Windows\SysWOW64\odbcad32.exe y presione retorno (enter) para el administrador odbc de 32 bits.


    Haga click en la pestaña DSN de Sistema (System DSN).


    Haga click en agregar (add).

    Seleccione el controlador apropiado.


    Enter your SQLite database path. Note that some of the options are documented at the driver site. I suggest leaving them as they are initially. Introduzca la ruta a su base de datos de SQLite. Note que las opciones estan documentadas en sitio web del controlador.


    Notice the 32 bit driver is only editable from a 32 bit administrator and the 64 bit driver is only editable from the 64 bit administrator. Note que el controlador de 32 bits solo es editable desde el administrador de 32 bit, así como el controlador de 64 bits solo es editable desde el administrador de 64 bits.


    Note que los botones remover (remove) y configurar (configure) estan deshabilitados.


  7. Crear el Linked Server en SQL Server

  8. A continuación mostramos el código T-SQL para crear el Linked Server a nuestra base de datos SQLite. Este tipo de Linked Server no necesita cuenta de acceso (Login) ni tampoco ningún contexto de seguridad.

    USE [master]
    GO
    EXEC sp_addlinkedserver
      
    @server = 'Mobile_Phone_DB_64', -- Nombre con el que identificaras el Link Server en SSMS
      
    @srvproduct = '', -- Puede estar en blanco pero no puede ser NULL
      
    @provider = 'MSDASQL',
      
    @datasrc = 'Mobile_Phone_DB_64' -- El nombre de la conexión DSN de Sistema
    GO

  9. Seleccionar los datos de la fuente e insertarlos en nuestra tabla en SQL Server

  10. Ahora haga click sobre el Linked Server y navegue hasta encontrar las tablas, luego simplemente ejecute sus consultas sobre las tablas.

    SELECT *
    FROM OPENQUERY(Mobile_Phone_DB_64 , 'select * from db_notes')
    GO


    Ahora usted puede utilizar una consulta con la clausula INTO para crear tablas con los datos que usted requiera en SQL Server.

    SELECT * INTO SQLite_Data -- Esto crea una tabla nueva con los datos seleccionados
    FROM OPENQUERY(Mobile_Phone_DB_64 , 'select * from db_notes')
    GO


    Luego verifique los tipos de datos en su tabla destino en SQL Server y cambie por los tipos correspondientes.

Referencias

martes, 22 de octubre de 2013

Migración de Accesos (Logins) con Contraseña entre Instancias de SQL Server

Autor: Alfredo Arias

Problema:

En nuestro día a día como DBAs, muchas veces nos encontramos con la necesidad de transferir todas o algunas de las bases de datos de un servidor a otro, ya sea por alguna falla a nivel de hardware o actualización de equipos o por una simple re-estructuración a nivel de infraestructura, sea cual sea el motivo de este cambio trae consigo muchos inconvenientes tanto administrativos como de seguridad, ya que al cambiar de servidor perderemos los logins asociados a nuestros usuarios en las bases de datos debido a que estos se encuentran relacionados a través del Security Identifier (SID) del login el cual se perderá si simplemente creamos otros logins en el servidor/instancia destino, sin contar que también perderemos las contraseñas de todos los accesos, recrear todos los logins, reasignar contraseñas, actualizar los archivos de configuración de todas las aplicaciones que accedían a estas bases de datos generaría una carga administrativa y un tiempo fuera de línea para nuestras aplicaciones, servicios y usuarios demasiado alta y a la vez inaceptable.


Solución:
  1. Conectate a la instancia de origen con SQL Server Management Studio.

  2. Abre una nueva ventana del Query Editor y corre el siguiente script. Este script crea dos procedimientos almacenados en la base de datos master. Estos procedimientos son sp_hexadecimal y sp_help_revlogin.

  3. USE MASTER
    GO
    IF OBJECT_ID ('sp_hexadecimal') IS NOT NULL
      
    DROP PROCEDURE sp_hexadecimal
    GO
    CREATE PROCEDURE sp_hexadecimal
        
    @binvalue VARBINARY(256),
        
    @hexvalue VARCHAR (514) OUTPUT
    AS
    DECLARE
    @charvalue VARCHAR (514)
    DECLARE @i INT
    DECLARE
    @length INT
    DECLARE
    @hexstring CHAR(16)
    SELECT @charvalue = '0x'
    SELECT @i = 1
    SELECT @length = DATALENGTH (@binvalue)
    SELECT @hexstring = '0123456789ABCDEF'
    WHILE (@i <= @length)
    BEGIN
      DECLARE
    @tempint INT
      DECLARE
    @firstint INT
      DECLARE
    @secondint INT
      SELECT
    @tempint = CONVERT(INT, SUBSTRING(@binvalue,@i,1))
      
    SELECT @firstint = FLOOR(@tempint/16)
      
    SELECT @secondint = @tempint - (@firstint*16)
      
    SELECT @charvalue = @charvalue +
        
    SUBSTRING(@hexstring, @firstint+1, 1) +
        
    SUBSTRING(@hexstring, @secondint+1, 1)
      
    SELECT @i = @i + 1
    END

    SELECT
    @hexvalue = @charvalue
    GO
      
    IF OBJECT_ID ('sp_help_revlogin') IS NOT NULL
      
    DROP PROCEDURE sp_help_revlogin
    GO
    CREATE PROCEDURE sp_help_revlogin @login_name SYSNAME = NULL AS
    DECLARE
    @name SYSNAME
    DECLARE
    @type VARCHAR (1)
    DECLARE @hasaccess INT
    DECLARE
    @denylogin INT
    DECLARE
    @is_disabled INT
    DECLARE
    @PWD_varbinary  VARBINARY (256)
    DECLARE @PWD_string  VARCHAR (514)
    DECLARE @SID_varbinary VARBINARY (85)
    DECLARE @SID_string VARCHAR (514)
    DECLARE @tmpstr  VARCHAR (1024)
    DECLARE @is_policy_checked VARCHAR (3)
    DECLARE @is_expiration_checked VARCHAR (3)

    DECLARE @defaultdb SYSNAME
      
    IF
    (@login_name IS NULL)
      
    DECLARE login_curs CURSOR FOR

          SELECT
    p.sid, p.name, p.TYPE, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
    sys.server_principals p LEFT JOIN sys.syslogins l
          
    ON ( l.name = p.name ) WHERE p.TYPE IN ( 'S', 'G', 'U' ) AND p.name <> 'sa'
    ELSE
      DECLARE
    login_curs CURSOR FOR


          SELECT
    p.sid, p.name, p.TYPE, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
    sys.server_principals p LEFT JOIN sys.syslogins l
          
    ON ( l.name = p.name ) WHERE p.TYPE IN ( 'S', 'G', 'U' ) AND p.name = @login_name
    OPEN login_curs

    FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
    IF (@@fetch_status = -1)
    BEGIN
      PRINT
    'No login(s) found.'
      
    CLOSE login_curs
      
    DEALLOCATE login_curs
      
    RETURN -1
    END
    SET
    @tmpstr = '/* sp_help_revlogin script '
    PRINT @tmpstr
    SET @tmpstr = '** Generated ' + CONVERT (VARCHAR, GETDATE()) + ' on ' + @@SERVERNAME + ' */'
    PRINT @tmpstr
    PRINT ''
    WHILE (@@fetch_status <> -1)
    BEGIN
      IF
    (@@fetch_status <> -2)
      
    BEGIN
        PRINT
    ''
        
    SET @tmpstr = '-- Login: ' + @name
        
    PRINT @tmpstr
        
    IF (@type IN ( 'G', 'U'))
        
    BEGIN -- NT authenticated account/group

          
    SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'
        
    END
        ELSE BEGIN
    -- SQL Server authentication
            -- obtain password and sid
                
    SET @PWD_varbinary = CAST( LOGINPROPERTY( @name, 'PasswordHash' ) AS VARBINARY (256) )
            
    EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT
            
    EXEC sp_hexadecimal @SID_varbinary,@SID_string OUT
      
            
    -- obtain password policy state
            
    SELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name
            
    SELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name
      
                
    SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' WITH PASSWORD = ' + @PWD_string + ' HASHED, SID = ' + @SID_string + ', DEFAULT_DATABASE = [' + @defaultdb + ']'

            
    IF ( @is_policy_checked IS NOT NULL )
            
    BEGIN
              SET
    @tmpstr = @tmpstr + ', CHECK_POLICY = ' + @is_policy_checked
            
    END
            IF
    ( @is_expiration_checked IS NOT NULL )
            
    BEGIN
              SET
    @tmpstr = @tmpstr + ', CHECK_EXPIRATION = ' + @is_expiration_checked
            
    END
        
    END
        
    IF (@denylogin = 1)
        
    BEGIN -- login is denied access
          
    SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )
        
    END
        ELSE IF
    (@hasaccess = 0)
        
    BEGIN -- login exists but does not have access
          
    SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )
        
    END
        IF
    (@is_disabled = 1)
        
    BEGIN -- login is disabled
          
    SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'
        
    END
        PRINT
    @tmpstr
      
    END

      FETCH
    NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
      
    END
    CLOSE
    login_curs
    DEALLOCATE login_curs
    RETURN 0
    GO


  4. Ejecute la siguiente instrucción:

  5. EXEC sp_help_revlogin;
    GO

    El resultado de ejecutar el procedimiento almacenado sp_help_revlogin es nuestro script de creación de logins, el cual nos servirá para crear en nuestro servidor/instancia destino los accesos o logins con los Security Identifiers (SID) y contraseñas originales, y por lo tanto nuestros logins y usuarios serán identicos a los de nuestro servidor/instancia original sin necesidad de re-mapear los usuarios a los accesos (logins), ni tampoco tendremos la necesidad de reasignar contraseñas, ni actualizar los archivos de configuración de nuestras aplicaciones.

  6. En el servidor destino, inicia SQL Server Management Studio, y conectate a la instancia de SQL Server destino (a la cual deseamos mover las bases de datos y logins con Security Identifiers (SID) y Contraseñas.

  7. Ejecute el script generado en el paso 3 en el servidor/instancia destino.


Referencias:

martes, 15 de octubre de 2013

Database Files Maxsize, Procedimiento almacenado para identificar archivos de base de datos por encima de umbral de tamaño especificado

Artículo original: http://www.mssqltips.com/sqlservertip/2359/find-sql-server-data-and-log-files-that-are-almost-out-of-space/
Autor: Eli Leiba
Traducido por: Alfredo Arias

Problema
En muchos servidores de SQL Server el tamaño máximo de los archivos, ya sea de datos o de log, puede estar restringido para asegurar que el espacio disponible en el servidor es adecuado. El problema con esto es que si tus archivos de datos o log se quedan sin espacio tendrás un error como el que se muestra a continuación y tus transacciones fallaran.

Solución
La solución general que sugiero es un procedimiento almacenado llamado dbo.obtener_archivos_bd_cerca_maxsize. Este procedimiento toma un parámetro opcional para el porcentaje de espacio, si ningún parámetro es suministrado este tomará un valor por defecto de 10%. Luego, este verificar cada archivo en cada una de las bases de datos en el servidor, incluyendo las bases de datos del sistema.

Si ejecutas el procedimiento almacenado sin pararle el parámetro este buscara todos los archivos de base de datos, tanto de datos como log, que tengan disponible 10% o menos de espacio, solo para los archivos que tengan configurado un máximo de espacio.

Aquí está el procedimiento almacenado, el cual puede ser creado en master o en su base de datos de administración de poseer alguna.

CREATE PROCEDURE [dbo].[obtener_archivos_bd_cerca_maxsize]
    
(@porcentaje_umbral_espacio DECIMAL (5,1) = 10.0)AS
BEGIN
     SET NOCOUNT ON
 
    
-- Crea tabla temporal global
    
CREATE TABLE ##todos_archivos_bd (
          
[dbname] SYSNAME,
    
[fileid] SMALLINT,
    
[groupid] SMALLINT,
    
[size] INT NOT NULL,
    
[maxsize] INT NOT NULL,
    
[growth] INT NOT NULL,
    
[status] INT,
    
[perf] INT,
    
[name] SYSNAME NOT NULL,
    
[filename] NVARCHAR(260) NOT NULL)
    
-- Itera sobre todas las bases de datos y recopila información
     -- de la vista del sistema 'sysfiles' y la introduce en la tabla
     -- temporal gblogal 'all_db_files' usando el procedimiento
     -- almacenado no documentado 'sp_MsForEachDB'
    
    
EXEC sp_MsForEachDB
          
@command1 = 'USE [$]; INSERT INTO ##todos_archivos_bd SELECT DB_NAME(), * FROM SysFiles',
          
@replacechar = '$'

    
-- Salida de los resultados
    
SELECT
          
[dbname] AS [database_name],
          
[name] AS [nombre_logico_archivo],
          
[filename] AS [ruta_archivo_fisico],
          
ROUND(size * CONVERT(FLOAT,8) / 1024,0) AS [tamano_actual_mb],
          
ROUND(maxsize * CONVERT(FLOAT,8) / 1024,0) AS [tamano_max_restringido_mb],
          
ROUND(maxsize * CONVERT(FLOAT,8) / 1024,0) - ROUND(size * CONVERT(FLOAT,8) / 1024,0) AS [espacio_restante_mb]
    
FROM   ##todos_archivos_bd
    
-- Omitir los archivos de base de datos que no tienen un
     -- tamaño máximo definido
    
WHERE     maxsize > -1
    
-- Encontrar los archivos de base de datos dentro del
     -- porcentaje establecido
    
AND ([maxsize] - [size]) * 1.0 < 0.01 * @porcentaje_umbral_espacio * [maxsize]
    
ORDER BY 6
    
    
DROP TABLE ##todos_archivos_bd
    
SET NOCOUNT OFF
END

GO

Este es un ejemplo de ejecución. Este muestra que ambos, tanto el archivo de datos como el de log están casi a su máximo de tamaño y que el archivo de datos tiene 3MB libres y el archivo de log tiene 4MB libres para la base de datos “test”.



Como administradores de bases de datos deberíamos ejecutar este procedimiento semanalmente o inclusive diariamente de ser posible para buscar todos los archivos de base de datos que estén acercándose al límite  máximo de tamaño. Entonces dependerá de nosotros resolver el problema agregando más espacio al archivo o tomando las medidas correctivas de lugar.

Idealmente el espacio del disco no debería de ser un problema y no deberías de preocuparte por el tamaño máximo de los archivos, pero incluso con el bajo costo del espacio en disco aun muchos sistemas están limitado en este aspecto y como administradores de bases de datos debemos de hacer el máximo con los recursos que poseemos.

Referencias:

jueves, 3 de octubre de 2013

Identificar ultima ejecución de procedimientos almacenados en las bases de datos de una instancia SQL Server


Problema

Debido a diferentes razones en nuestras bases datos se acumulan procedimientos almacenados que quedan en desuso, lo cual se traduce a su vez en riesgos de seguridad y problemas de control de código, por lo que debemos identificar cuáles son los procedimientos almacenados que no están siendo utilizados por nuestras aplicaciones y tomar medidas administrativas en este sentido 

Solución

La siguiente consulta itera en todas las bases de datos de la instancia en la cual se ejecute excluyendo las bases de datos del sistema, buscando la fecha de la última ejecución de los procedimientos almacenados de estas en la vista de administración dinámica sys.dm_exec_procedure_stats, excluyendo los procedimientos almacenados que tengan los prefijos dt_ (utilizados para soportar los diagramas de bases de datos) y sp_ (considerado una mala práctica por razones que todos conocemos) en sus nombres, debemos de tener en cuenta que esta vista solo muestra este dato en relación a la última vez que nuestra instancia se reinició, por lo tanto la información que obtendremos será relativa a esta fecha y puede que nos muestre que muchos de los procedimientos no han sido ejecutados nunca, lo cual en realidad significa que no se han ejecutado desde la última vez que se reinició la instancia, independientemente de esta situación, la información que obtenemos de esta consulta es muy útil ya que nos dará una idea del comportamiento de uso de nuestros procedimientos almacenados y nos ayudara a tomar decisiones de control del código esta en uso lo cual es un tema de seguridad a nivel de código muy importante.


USE MASTER;
GO

IF OBJECT_ID('tempdb..#temp') IS NOT NULL
      
DROP TABLE #temp;
      
CREATE TABLE #temp ([database_name] NVARCHAR(255), [procedure_name] NVARCHAR(255), last_execution_time DATETIME);

EXEC sp_MSForEachDB '  
      USE [?];

      IF ''[?]'' NOT IN (''[master]'', ''[model]'', ''[msdb]'', ''[tempdb]'')
      BEGIN
            INSERT INTO #temp
                  SELECT
                        ''[?]''
                        ,p.name
                        ,last_execution_time
                  FROM sys.procedures AS p
                  LEFT OUTER JOIN sys.dm_exec_procedure_stats AS s
                  ON    p.[object_id] = s.[object_id]
                  WHERE SUBSTRING(p.name,1,3) not in (''dt_'',''sp_'')
                  ORDER BY  p.name, s.last_execution_time;
      END'
;


SELECT
  
[database_name]
  
,[procedure_name]
  
,[last_execution_time]

FROM #temp;

IF OBJECT_ID('tempdb..#temp') IS NOT NULL
      
DROP TABLE #temp;

GO


Referencias

martes, 1 de octubre de 2013

Consulta para obtener el nombre de usuario de la cuenta del servicio SQL Server

Artículo Original: Query to find out Service Account details
Traducido por: Alfredo Arias

Problema

En ocasiones debemos conocer cuál es el usuario con el cual corre la cuenta del servicio de SQL Server en nuestro servidor, cuenta que como buena práctica debería de ser una cuenta de dominio. En mi búsqueda sobre cómo encontrar esta información utilizando una consulta, ya que muchas veces no tenemos acceso al sistema operativo de nuestro servidor sino solo acceso a conectarnos a la instancia que estamos administrando a través de Management Studio encontré esta solución la cual consiste en un script que lee este dato directamente desde el Registro de Windows.


Solución

Obtendremos la cuenta del servicio recuperándola de la clave del registro antes mencionada utilizando el procedimiento almacenado extendido xp_instance_regread, a continuación tenemos un ejemplo de cómo obtener dicha información de una instancia de SQL Server por Defecto.


DECLARE @ServiceaccountName VARCHAR(250)

EXECUTE MASTER.dbo.xp_instance_regread
          
N'HKEY_LOCAL_MACHINE',
          
N'SYSTEM\CurrentControlSet\Services\MSSQLSERVER',
          
N'ObjectName',
          
@ServiceAccountName OUTPUT,
          
N'no_output'

SELECT @ServiceaccountName

jueves, 4 de abril de 2013

IF EXISTS, Verificar Si Una Tabla Temporal Existe SQL Server

IF EXISTS, Verificar Si Una Tabla Temporal Existe en SQL Server

Artículo original: http://sqlservercodebook.blogspot.com/2008/03/check-if-temporary-table-exists.html
Traducido por: Alfredo Arias

Verificar Si Una Tabla Temporal Existe

¿Como verifico si una tabla temporal existe tanto a nivel local como a nivel global en SQL Server? Para hacerlo puedes usar IF EXISTS

 
IF OBJECT_ID('tempdb..#temp') IS NOT NULL  
¡Veamos como funciona!
 -- Cambiamos el contexto al de nuestra base de datos de ejemplo  
 USE Norhtwind  
 GO  
 -- Creamos una tabla temporal dentro de esta  
 CREATE TABLE #temp(id INT)  
 -- Verificamos si nuestra tabla existe  
 IF OBJECT_ID('tempdb..#temp') IS NOT NULL  
   BEGIN  
    PRINT '¡#temp existe!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡#temp does not exist!'  
   END  
 -- Otra forma de verificar si esta tabla existe es   
 -- utilizando un segundo parametro opcional no documentado  
 IF OBJECT_ID('tempdb..#temp','u') IS NOT NULL  
   BEGIN  
    PRINT '¡#temp existe!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡#temp no existe!'  
   END  
 -- No hagas esto porque esto verificara la base de datos  
 -- local y por lo tanto retornara no existe   
 IF OBJECT_ID('tempdb..#temp','local') IS NOT NULL  
   BEGIN  
    PRINT '¡#temp existe!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡#temp no existe!'  
   END  
 -- A menos que cambiemos el contexto de la   
 -- base de datos a algo como esto  
 USE tempdb  
 GO  
 -- Ahora la tabla existe de nuevo  
 IF OBJECT_ID('tempdb..#temp','local') IS NOT NULL  
   BEGIN  
    PRINT '¡#temp exists!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡#temp no existe!'  
   END  
 -- Volvamos a Norhtwind de nuevo  
 USE Norhtwind  
 GO  
 -- Verificamos si la tabla temporal existe  
 IF OBJECT_ID('tempdb..#temp') IS NOT NULL  
   BEGIN  
    PRINT '¡#temp exists!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡#temp does not exist!'  
   END  
 Ahora abre una nueva ventana del Analizador de Consultas (Query Analyzer) (CTRL + N) y ejecuta este c&oacute;digo de nuevo.  
 -- Verificar si existe  
 IF OBJECT_ID('tempdb..#temp') IS NOT NULL  
   BEGIN  
    PRINT '¡#temp existe!'  
   END  
 ELSE   
   BEGIN  
    PRINT '¡#temp no existe!'  
   END  
 La tabla no existe y es correcto ya que esta es una tabla temporal de alcance local, no una tabla temporal de alcance global.  
 Probemos ahora esta otra sentencia.  
 -- Crea una tabla temporal de alcance global   
 -- Note los 2 signos de número, esto crea la tabla a nivel global  
 CREATE TABLE ##temp(id INT)   
 -- Verifica si esta existe  
 IF OBJECT_ID('tempdb..##temp') IS NOT NULL  
   BEGIN  
    PRINT '¡##temp existe!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡##temp no existe!'  
 END  
 [/sql]  
 &iquest;Existe, correcto?  
 Ahora ejecutamos el mismo c&oacute;digo en una ventana nueva del Analizador de Consultas (Query Analyzer) (CTRL + N).  
 -- Verificamos si existe  
 IF OBJECT_ID('tempdb..##temp') IS NOT NULL  
   BEGIN  
    PRINT '¡##temp existe!'  
   END  
 ELSE  
   BEGIN  
    PRINT '¡##temp no existe!'  
   END  
 Y si esta esta vez existe ya que esta es una tabla de alcance global.