Blog Maxi Accotto

Blog , cursos, coaching y Consultoria en SQL Server

 

 

 

Como denegar el acceso de los Administradores locales en Analysis Service

Como todos sabrán en SQL Server Analysis Service (SSAS) por defecto los administradores local del equipo también lo son de SSAS, ahora bien, en muchas empresas esto no es deseable para lo cual queremos que los Admin del equipo no tengan acceso sobre el SSAS.

Este post indica paso a paso de como deshabilitar a los administradores.

· Inicie el SSMS (Sql Server Management Studio)

· Conecte contra el servidor de SSAS (debe tener permisos para poder hacerlo claro)

· Vaya a las propiedades del servidor pulsando el botón alterno del mouse

· Acceda a las opciones de Seguridad y deje ahí solo los administradores del SSAS

clip_image002

Ahora que ha definido a los Administradores del SSAS lo que debemos hacer es deshabilitar a los admin locales, por mas que los hayamos sacado de la opción security también es necesario deshabilitarlo desde las opciones avanzadas, entonces

· Vaya a la solapa general y pulse el botón “Show Advanced (all) Properties”

clip_image004

· Busque ahora dentro de la grilla de opciones la que dice “Security \ BuiltinAdminsAreServerAdmins” y ponga su valor en false

clip_image006

Con esto ya los administradores locales no tendrán acceso a nuestro SSAS.

Currently rated 1.0 by 1 people

  • Currently 1/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Tuesday, October 06, 2009 10:37 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Pasando Logins entre servidores SQL

La tarea de pasar Login entre servidores es algo casi habitual entre los DBA, esta tarea se puede deber a migraciones de versiones, cambios de servidores, etc..

Como todos sabrán SQL Server dispone de dos tipos de Logins, los de Windows y los de SQL.

Bien, como pasamos los logins de un servidor a otro? pues Microsoft ha publicado unos script muy interesantes para estas tareas, uno para SQL 2000 y el otro para SQL 2005 / 2008.

Los Script están muy buenos de verdad pero carecen de una funcionalidad muy importante para mi gusto que es pasar los roles de esos login, pues bien como esta tarea es repetitiva me he tomado el trabajo de modificar estos dos script y agregarle la funcionalidad faltante.

Es importante usar estos Script ya que pasan datos importantes como la Password y el SID, este ultimo asegura que luego no tengamos problemas de usuarios huérfanos

 

Currently rated 3.3 by 3 people

  • Currently 3.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Sunday, October 04, 2009 4:51 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Error inesperado en Sharepoint consumiendo reportes de Reporting Service

Si tienen integrado a Sherepoint (ya sea el portal o bien el services) son Reporting services y ejecutan un reporte que dura mas de dos minutos les aparecerá del lado de Sharepoint el siguiente error: Error inesperado o bien “An unexpected error has occurred” en ingles.

Por defecto existe un timeout a nivel ASP que es de 2 minutos, para cambiar este valor habrá que hacer los siguientes pasos desde el servidor de Sharepoint

  1. Abrir el archivo web.config de la aplicación sharepoint (por ejemplo \inetpub\wwwroot\wss\VirtualDirectories\80)
  2. Buscar dentro del archivo httpRuntime
  3. Agregar executionTimeout con un valor en segundos deseado, por ejemplo 1800

<httpRuntime maxRequestLength="51200" executionTimeout = "1800" />

Esto nos permitirá extender el timeout del sharepoint haciendo que si tenemos reportes con una duración mayor a dos minutos los podamos ejecutar sin errores. Les recomiendo poner 1800 segundos ya que también ese valor es el predeterminados a nivel Reporting Service para un timeout de Reporte.

Aquí les dejo el link original de donde he sacado la solución y me ha funcionado perfecto.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Thursday, July 09, 2009 8:46 AM
Permalink | Comments (13) | Post RSSRSS comment feed

Generando Script de estructuras y datos con el Management Studio

Una de las operaciones que muchas veces necesitamos hacer es la de poder generar script de las estructuras de nuestros objetos o bien de toda una base de datos.

Esta opción esta disponible desde viejas versiones del producto donde con el Enterprise Manager o bien el Management Studio de 2005 se podía hacer lo mas bien.

Pero hasta SQL 2008 lo único que nos dejaba generar script eran justamente estructuras y no datos de ellas (registros).

Imaginemos que tenemos una tabla de configuración que necesitamos además de hacer el script de las estructuras que también incluya los registros, para lo cual se debería transformar en sentencias INSERT de nuestro script .SQL.

Para poder hacer este tipo de tareas por lo general debíamos recurrir a herramientas de terceras partes, ahora y a partir de SQL Server 2008 usando el Management Studio (el de Express también lo soporta y es gratuito) podemos a nuestros script darle la posibilidad de generar datos.

Aquí les muestro como lo hacemos

Primero debemos entrar al SSMS (SQL Server Management Studio) para conectarnos al servidor.

Luego sobre la solapa base de datos, pulsaremos el botón alterno del mouse para seleccionar Task y luego Generate Script el cual nos aparecerá el siguiente Wizard.

image

Lo que aquí debemos seleccionar es la base de datos a la cual necesitamos armar los script, por ejemplo MUG1 y luego pulsar sobre el botón siguiente, al hacer esto nos aparecerá una segunda ventana con distintas opciones, por ejemplo si queremos generar los script de índices, los logins, etc.

En el SSMS de 2008 tenemos un nuevo chiche y es que una de esas opciones en la sección de tablas es de generar la data, debemos poner en true (viene en false por defecto)

image

Al seleccionar esa opción ya nuestro script resultante no solo incluirá las estructuras sino que también los datos con sentencias INSERT.

La siguiente ventana nos permite seleccionar que tipos de objetos queremos hacer el script, para este ejemplo seleccionaremos Tables

image

Al seguir al nuevo paso tendremos el listado de tablas las cuales podemos seleccionar o una o mas de una

image

Por ultimo nos preguntara el wizard donde queremos enviar el resultado, aquí yo le pondré en una nueva ventana para que podamos observarlo

image

Bien, luego de hacer esto y dejar que termine el proceso, tendremos en nuestra nueva ventana el script resultante.

Aquí copio mi resultado donde se podrá observar que no solo armo las sentencias DDL para la tabla sino que también las DML para insertar el registro que tiene la tabla.

USE [MUG1]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO

CREATE TABLE [ventas].[Clientes](
    [Codigo] [varchar](15) NOT NULL,
    [Nombre] [varchar](100) NULL,
    [Direccion] [varchar](100) NULL,
    [fecha] [date] NULL,
    [id] [int] IDENTITY(100,10) NOT NULL,
    [fecha_nac] [date] NULL,
 CONSTRAINT [PK_Clientes_1] PRIMARY KEY CLUSTERED 
(
    [Codigo] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [ventas].[Clientes] ON
INSERT [ventas].[Clientes] 
([Codigo], [Nombre],[Direccion], 
[fecha], [id], [fecha_nac]) 
VALUES (N'1', N'mug', N'riv...', NULL,
100, CAST(0x16310B00 AS Date))

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Thursday, July 09, 2009 6:22 AM
Permalink | Comments (7) | Post RSSRSS comment feed

Sql Dinámico y seguridad en SQL2005

 

SQL-Dinámico y seguridad dentro de SQLServer 2005

El SQL-dinámico es una técnica la cual nos permite ejecutar sentencias TSQL de forma dinámica,  para poder hacer uso de esta técnica es necesario utilizar o bien el store procedure sp_executesql o sino EXEC(@str).

Este articulo no tiene como objetivo entrar en detalle del SQL-dinámico ya que hay otros escritos al respecto como por ejemplo el siguiente link el cual recomiendo leer

 

Aquí nos concentraremos en la seguridad y los cambios que hay en SQL2005 al respecto.

Una de las mayores contras que tiene el uso de SQL-Dinámico es que si lo ponemos dentro de un Store Procedure el usuario que ejecute tal Store no solo deberá tener permisos de execute sino que también deberá tener permisos sobre los objetos que haga referencia el SQL-Dinámico.

Para poder ser más específicos veamos un ejemplo al respecto

USE

MASTER

GO

CREATE DATABASE TEST1

GO

USE TEST1

GO

-- CREAMOS UN NUEVO LOGIN

CREATE

LOGIN FEDERICO WITH PASSWORD ='PASSW@RD'

GO

USE TEST1

GO

-- CREAMOS EL USUARIO EN LA BASE DE DATOS

CREATE USER FEDERICO

GO

-- CREAMOS UNA TABLA

CREATE

TABLE DBO.EMPLEADOS (ID INT IDENTITY, NAME VARCHAR(50))

GO

-- CREAMOS UN STORE QUE CONSUME LA TABLA EMPLEADOS

CREATE

PROC DBO.USP_EMPLEADOS_GET_ALL AS
SELECT * FROM DBO.EMPLEADOS

GO

-- CREAMOS OTRO STORE QUE CONSUME LA TABLA PERO CON SQLDINAMICO

CREATE

PROC DBO.USP_EMPLEADOS_GET_ALL2 AS

DECLARE @N NVARCHAR(50)

SET @N = N'SELECT * FROM DBO.EMPLEADOS'
EXECUTE SP_EXECUTESQL @N

GO

-- FEDERICO SOLO TIENE PERMISOS A LOS STORES

GRANT

EXECUTE ON DBO.USP_EMPLEADOS_GET_ALL TO FEDERICO
GRANT EXECUTE ON DBO.USP_EMPLEADOS_GET_ALL2 TO FEDERICO

GO

-- ENTRAMOS COMO FEDERICO

EXECUTE

AS LOGIN = 'FEDERICO'

SELECT

SUSER_SNAME()

SELECT

* FROM DBO.EMPLEADOS -- FALLA PORQUE NO TENEMOS PERMISO

EXEC

DBO.USP_EMPLEADOS_GET_ALL -- FUNCIONA

EXEC

DBO.USP_EMPLEADOS_GET_ALL2 -- FALLA POR EL SQL-DINAMICO

REVERT

-- REVERTIMOS EL LOGIN

Como se ha podido observar en el ejemplo el segundo Store ha fallado cuando el usuario Federico lo invoco ya que no tenia permisos sobre los objetos internos del mismo, en este caso la tabla "Empleados".

Esto atenta contra el buen uso de un Store procedure ya que una de sus virtudes es aislar a los usuarios del acceso directo a los objetos, con lo cual si deseamos en este caso usar SQL-Dinámico deberíamos darle permisos de Select a la tabla correspondiente.

Esto funciona así en 2000 y no hay solución al respecto, por lo cual yo no recomiendo mucho el uso de SQL-Dinámico y de usarlo tener en claro lo que está sucediendo.

¿Que cambios hay en 2005?

SqlServer 2005 permite dentro de un objeto como el Store Procedure indicarle el contexto de ejecución, esto es totalmente nuevo y se indica con la sentencia WITH EXECUTE AS (Caller, Owner o Username)

  • Caller es utilizado por defecto y representa a quien llama (como vimos en el ejemplo 1)
  • Owner indica que se impersonificara como el usuario propietario del objeto, en este caso como entre con la cuenta de SA entonces estamos impersonificando con ella.
  • User_name nos permite indicar con que login deseamos que se impersonifique

 

El siguiente ejemplo modifica el  Store con SQL Dinámico pero utilizando Execute AS

-- CAMBIOS EL STORE DEL SQL DINAMO PARA QUE SE EJECUTE EN

-- OTRO CONTEXTO DE SEGURIDAD

ALTER PROC DBO.USP_EMPLEADOS_GET_ALL2

WITH EXECUTE AS OWNER

AS

DECLARE @N NVARCHAR(50)

SET @N = N'SELECT * FROM DBO.EMPLEADOS'
EXECUTE SP_EXECUTESQL @N

GO

-- VOLVEMOS A PROBAR

EXECUTE

AS LOGIN = 'FEDERICO'

SELECT

SUSER_SNAME()

SELECT

* FROM DBO.EMPLEADOS -- FALLA PORQUE NO TENEMOS PERMISO

EXEC

DBO.USP_EMPLEADOS_GET_ALL

EXEC

DBO.USP_EMPLEADOS_GET_ALL2

REVERT

Aquí se ve claramente que ahora Federico no necesito permisos sobre la tabla "Empleados" para poder hacer uso del SQL-Dinámico.

Con esto lo que logramos es seguir manteniendo una de las enormes virtudes que tienen los Stores Procedures que es el aislamiento con respecto a los objetos base.

No confundir el Execute AS del Store con el luego utilizado en el query para dentro del management Studio impersonificarnos como Federico sin la necesidad entrar a otra ventana.

Estos ejemplos deben ser probados desde SQL2005, si desea probar el primer ejemplo desde SQL2000 deberá sacar la sentencia Execute AS y probar la seguridad entrando con el correspondiente login y password.

Bueno espero les sea de utilidad y espero sus comentarios.

Nos vemos!

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Thursday, July 09, 2009 2:47 AM
Permalink | Comments (16) | Post RSSRSS comment feed

Como generar el numero de nuestros comprobantes sin morir en el intento

En la mayoría de nuestras aplicaciones tenemos la necesidad de poder generar el numero de nuestros comprobantes (Ordenes de compra, pedidos de ventas, etc.)

Existen diferentes técnicas para poder realizar esta operación. En los ejemplos de este post mostrare las dos formas mas tradicionales de hacerlo y cuales son sus pros y contras de cada una.

Para estos ejemplos crearemos una base de datos nueva con la siguiente sentencia

CREATE

DATABASE EJEMPLO_NUMERACION

Usando auto numéricos

Dentro de SQLserver podemos definir en un tipo de dato entero la posibilidad de que sea autoincrementar, esto hará que de forma automática el motor de base de datos calcule el valor que debe ir en ese campo. Usando esta posibilidad podríamos definir que en nuestros comprobantes el campo de numero sea auto numérico.

El siguiente ejemplo ilustra como hacer esta operación

USE

EJEMPLO_NUMERACION

GO

CREATE

TABLE OC (ID INT IDENTITY, FECHA DATETIME NOT NULL,

CLIENTE VARCHAR(50) NOT NULL)

GO

Como se podrá observar el campo ID es de tipo int y a su vez tiene la propiedad de ser autoincremental, el resto de los campos corresponden a datos necesarios de la Orden de compra de este ejemplo.

Si queremos insertar un nuevo registro (o sea una nueva orden de compra) simplemente hacemos lo siguiente

INSERT

INTO OC (FECHA,CLIENTE)

VALUES

(GETDATE(),'IBM')

go

Si luego de esto hacemos un select a la tabla OC y vemos los registros podremos observar que se ha generado de forma automática el ID 1, si hacemos otro insert valido veremos que se generara el 2 y así sucesivamente.

Ahora bien, que sucede si por ejemplo hacemos un insert y no se pudo realizar la transacción, por ejemplo queremos insertar una OC donde el campo Cliente sea nulo lo cual no esta permitido por  la integridad de la base de datos.

INSERT

INTO OC (FECHA,CLIENTE)

VALUES

(GETDATE(),NULL)

go

Al hacer esta instrucción recibiremos el siguiente error:

Msg 515, Level 16, State 2, Line 2

Cannot insert the value NULL into column 'CLIENTE', table 'EJEMPLO_NUMERACION.dbo.OC'; column does not allow nulls. INSERT fails.

 

Bien, el registro si hacemos un select a la tabla no se ha generado y eso es correcto ya que ha existido un problema.

Ahora bien, hasta el momento solo tenemos generada la OC con ID = 1 y la 2 ha fallado. Pues arreglamos el tema del null en el insert para que se genere la OC = 2 ya que es la que continua luego de la 1.

INSERT

INTO OC (FECHA,CLIENTE)

VALUES

(GETDATE(),'MICROSOFT')

go

El registro se genero ahora "correctamente", pero si hacemos un select a la tabla nos encontraremos que ahora tenemos el ID = 1 y el ID = 3, con lo cual estamos teniendo huecos en nuestra numeración.

Como se podrá observar es un grave problema tener estos huecos en los números de los comprobantes (imagine que fueran facturas por ejemplo), pues esto se debe a como funciona el auto númerico de SQLServer, es simple de usar e implementar pero hay que saber lo de las transacciones, ante una falla el numero lo reservo igual y considero que se genero con lo cual lo salta [:(]

Usando tabla numeradora

Otra alternativa a la que vimos es la de generar una tabla donde tendremos los números de los comprobantes y usarla para numerar uno nuevo, si la transacción fallo simplemente hay que tener el recaudo de no cambiar el valor en la tabla numeradora.

Entonces, lo primero que generamos es esta tabla numeradora

CREATE

TABLE NUMERADOR (TIPO_COMPROBANTE VARCHAR(100),

ULTIMO_NUMERO INT)

GO

INSERT

INTO NUMERADOR VALUES ('OC',1)

INSERT

INTO NUMERADOR VALUES ('FACTURAS',10)

GO

La tabla numeradora podrá ser utilizada por distintos comprobantes, de ahí es que tiene un campo denominado tipo_comprobante y otro ultimo_numero.

Como también se puede observar se le han insertado 2 registros, uno para los comprobantes de Ordenes de Compra y el otro para facturas.

Ahora bien, lo que debemos hacer como siguiente paso es utilizar esta tabla numeradora para obtener el ultimo_numero sumarle 1 y asignarlo a la OC que estamos generando. Aquí entran a jugar una serie mas de detalles a conocer, si hacemos un Select (sin cambiar el nivel de bloqueo) y otro usuario en otro sitio hace lo mismo podrán obtener el mismo ultimo_numero lo cual al sumarle 1 los dos intentaran hacer la misma OC.

Para evitar este tipo de situaciones, ya que no solo necesito numerar sino también asegurar que no exista la posibilidad de obtener números duplicados (la misma OC por ejemplo) es que vamos a hacer uso de algunos trucos de bloqueos y variables.

DROP

TABLE OC

GO

CREATE

TABLE OC (NUMERO INT, FECHA DATETIME,CLIENTE VARCHAR(50) NOT NULL)

GO
declare @proximo_numero int
begin try

begin

tran

update numerador set @proximo_numero = ultimo_numero = ultimo_numero + 1

where tipo_comprobante = 'OC'

INSERT INTO OC VALUES (@proximo_numero,getdate(),'SQLTOTAL')

commit tran
end try

begin

catch

rollback tran -- primero hacemos un rollback

print @@error -- mostramos el error generado

end

catch

GO

La sentencia UPDATE no solo modifica la tabla sino que además actualiza la variable que luego utilizamos para generar el comprobante. De esta manera logramos que si hay 2 procesos haciendo esto por el uso de los bloqueos internos de SQL al hacer uso de la instrucción UPDATE, el segundo proceso no podrá hacer update hasta que el primero no haya terminado la transacción (ya sea por commit o rollback tran)

Si hacemos un select sobre OC vamos a observar que se ha generado el id numero 2, ahora bien si intentamos hacer un nuevo insert pero que falla y luego volvemos a insertar uno que no falla no tendremos los huecos en la numeración como nos ha sucedido con los identity.

Para probar que no se van a generar 2 números iguales en tiempos iguales hay que hacer la siguiente prueba con el Script.

1) Habrá dos ventanas de management studio o Query Analizer si usa 2000

2) En ambas copie por ejemplo este código

declare @proximo_numero int begin try

begin

tran

update numerador set @proximo_numero = ultimo_numero = ultimo_numero + 1

where tipo_comprobante = 'OC'

INSERT INTO OC VALUES (@proximo_numero,getdate(),'SQLTOTAL')
end try

begin

catch

rollback tran -- primero hacemos un rollback

print @@error -- mostramos el error generado

end

catch

3) Ejecute desde la primer ventana el script, luego hágalo desde la segunda. Como podrá observar en la segunda ventana se queda esperando y no procesa , esto se debe que hay bloqueos sobre la tabla numeradora y que el segundo update no ha podido ser realizado.

4) En la primer ventana escriba commit tran y ejecute solo eso

Al hacer esto podrá observar que la segunda ventana ha terminado.

5) Haga un select sobre OC y vea los resultados [:)]

 

Bueno con esto termino, espero que les haya sido de utilidad y espero sus comentarios

 

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Thursday, July 09, 2009 2:44 AM
Permalink | Comments (14) | Post RSSRSS comment feed

Error al querer eliminar un job de SQL Server 2005

Al intentar eliminar un Job del SQL Server 2005 puede aparecer el siguiente mensaje de error

The DELETE statement conflicted with the REFERENCE constraint “FK_subplan_job_id”. The conflict occurred in database “msdb”, table “dbo.sysmaintplan_subplans”, column ‘job_id’.
The statement has been terminated. (Microsoft SQL Server, Error: 547)

nos indica que no se puede eliminar un job por problemas con los planes de mantenimiento.

Para poder resolver este problema y poder eliminar el Job les dejo el siguiente Script

declare @job_name varchar(500)

set @job_name = N'tujob' ;

 

USE [msdb]

delete sysmaintplan_log

FROM sysmaintplan_subplans AS subplan

INNER JOIN

sysjobs_view AS jobs ON subplan.job_id = jobs.job_id INNER JOIN

sysmaintplan_log ON subplan.subplan_id = sysmaintplan_log.subplan_id

WHERE (jobs.name = @job_name)

 

delete sysmaintplan_subplans

FROM sysmaintplan_subplans AS subplan INNER JOIN

sysjobs_view AS jobs ON subplan.job_id = jobs.job_id

WHERE (jobs.name = @job_name)

 

delete

from msdb.dbo.sysjobs_view where name = @job_name

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Thursday, July 09, 2009 2:34 AM
Permalink | Comments (9) | Post RSSRSS comment feed

Comprendiendo el funcionamiento de Transacciones en SQL Server

El otro día mientras daba un curso en una empresa para desarrolladores, vi que algunas cosas de las transacciones no se comprendían bien y francamente me llamo la atención.

Luego de ese curso pensé en explicar en mi blog como se manejan las transacciones dentro de SQL Server y que cuidados tener, y bueno aquí estamos smile_teeth

 

¿Porque usamos transacciones?

Bueno aquí la cosa parece simple verdad, básicamente usamos transacciones para poder asegurar integridad de una operación, si por ejemplo necesitamos guardar una factura y esta se compone de Cabecera y Detalle no se debería guardar si hubo algún error en el detalle.

También podríamos mencionar otros ejemplos, donde se necesitan hacer mas de una operación y de fallar alguna no aplicar nada.

Tenemos dos tipos de transacciones en SQL Server, las implícitas y las explicitas.

Las primeras son aquellas que no debemos indicarle al SQL que hacemos una transacción pero el lo genera, por ejemplo como el siguiente caso.

DELETE FROM CLIENTES
WHERE PAIS = 'ARGENTINA'

Aquí se ejecutara el delete para todos los clientes que cumplan la condición de Argentina, si hay un error al intentar borrar un cliente se hará un rollback y no se borrara ninguno, esto se debe a que el delete al igual que el insert y update internamente arman una transacción.

Las transacciones explicitas son aquellas que nosotros indicamos con la sentencia Begin Transaction / Commit o Rollback Transaction.

BEGIN TRANSACTION 
  INSERT INTO EMPLEADOS (ID, NOMBRE)
  VALUES (1,'CONDUIT')
  
  DELETE FROM AUDITORIA
  WHERE EMPLEADO = 1
COMMIT TRAN

¿Como funciona el Commit y Rollback?

El commit es el que confirmara la transacción y el rollback es el que la deshará, pero aquí hay algunos detalles muy importantes en su comportamiento cuando usamos transacciones anidadas.

Primero veamos un ejemplo simple

CREATE TABLE #T1 (ID INT, NOMBRE VARCHAR(50))
GO

CREATE TABLE #T2 (ID INT, FECHA DATETIME NOT NULL)
GO

BEGIN TRY

    BEGIN TRAN   
    
      INSERT INTO #T1 VALUES (1,'CONDUIT')
      INSERT INTO #T1 VALUES (1,GETDATE())
    
    COMMIT TRAN
END TRY

BEGIN CATCH
  ROLLBACK TRAN
  SELECT @@ERROR 
END CATCH
 

En el ejemplo básicamente hacemos dos insert y si hay algún error entramos en la sección del Catch y lo primero que hacemos es el rollback para luego mostrar el error.

Este es un simple ejemplo donde si no hay problemas se harán los dos insert y el commit los confirmara y de haber un error en algún insert se hará Rollback.

Anidar transacciones

Hasta aquí vimos dos ejemplos bastantes simples del manejo de transacciones. Ahora bien que sucede si necesitamos manejar transacciones anidadas, por ejemplo ejecutamos un Store que abre una transacción que llama a otro Store que abre otra transacción y así, o bien en el mismo código.

Aquí primero debemos comprender como impacta un Rollback y un Commit y es donde mayor confusión quizás hay.

El Rollback hará que se deshagan todas las transacciones o sea si armamos una transacción dentro de otra y la segunda hace un rollback hará que también la primera lo haga, en cambio el commit lo hará por cada bloque a menos que apliquemos un commit en el bloque externo lo cual hará que todo se comitee.

Para poder evaluar cuantas transacciones tenemos abiertas podemos utilizar la variable

@@Trancount

Para ser mas ejemplificado con este tema vamos a ver un poco de código, el primer ejemplo muestra como funciona el Rollback

 

USE TEMPDB
go

CREATE TABLE Cabecera (id int, fecha Datetime)
go

CREATE TABLE Detalle (CabeceraId int, linea int NOT NULL,
                      Cantidad decimal (8,2) NOT NULL)
GO

CREATE TABLE Auditoria (Id int identity,
                        Fecha datetime not null
                       ) 

-- Ejecute el Script por pasos para ir comprendiendo


-- ======================================= 
--      Funcionamiento del Rollback
-- =======================================

-- Vemos que no hay Transacciones
SELECT @@TRANCOUNT 

-- Abrimos una transacción
BEGIN TRAN
  -- hacemos los insert
  INSERT INTO Cabecera 
  VALUES (1,GETDATE())
  
  INSERT INTO Detalle 
  Values (1,1,100)
  
  -- vemos las transacciones abiertas (1)
  SELECT @@TRANCOUNT 

  -- Abrimos otra transacción
  BEGIN TRAN 
    -- Verificamos que Trancount se incremento en 1
    SELECT @@TRANCOUNT   
    
    -- Hacemos un Rollback
   ROLLBACK TRAN
  
    -- Verificamos que Trancount quedo en 0
    -- Lo cual ha hecho un rollback de todo
    SELECT @@TRANCOUNT   
go    

El siguiente ejemplo muestro como funciona el Commit con el manejo de transacciones

 
-- ======================================= 
--      Funcionamiento del Commit
-- =======================================

-- Vemos que no hay Transacciones
SELECT @@TRANCOUNT 

-- Abrimos una transacción
BEGIN TRAN
  -- hacemos los insert
  INSERT INTO Cabecera 
  VALUES (1,GETDATE())
  
  INSERT INTO Detalle 
  Values (1,1,100)
  
  -- vemos las transacciones abiertas (1)
  SELECT @@TRANCOUNT 

  -- Abrimos otra transacción
     BEGIN TRAN 
    -- Verificamos que Trancount se incremento en 1
     SELECT @@TRANCOUNT   
    
    -- Hacemos un Rollback
     INSERT INTO Auditoria (Fecha)
     VALUES (GETDATE())
  
     COMMIT TRAN
    -- Verificamos que Trancount decrecio en 1
     SELECT @@TRANCOUNT   

COMMIT TRAN  -- Hacemos el utimo Commit
  
-- Verificamos que Trancount queda en 0
SELECT @@TRANCOUNT   
go    
  

Hasta que pudimos observar con ejemplos la diferencia de comportamiento que tenemos entre el Rollback y el Commit

como hemos explicado en el inicio del articulo.

Ahora bien, en el siguiente ejemplo muestro como manejar transacciones anidadas sin que el rollback se vaya a 0 y que lo haga a un punto que yo determine.

-- ======================================= 
--      Manejo de transacciones Anidadas
-- =======================================

-- Vemos que no hay Transacciones
SELECT @@TRANCOUNT 

-- Abrimos una transacción
BEGIN TRAN A
  -- hacemos los insert
  INSERT INTO Cabecera 
  VALUES (1,GETDATE())
  
  INSERT INTO Detalle 
  Values (1,1,100)
  
  -- Hacemos un punto de salvacion
  
  SAVE TRAN B
  
  -- vemos las transacciones abiertas (1)
  SELECT @@TRANCOUNT 

  -- Abrimos otra transacción
     BEGIN TRAN C
    -- Verificamos que Trancount se incremento en 1
     SELECT @@TRANCOUNT   
    
    -- Hacemos un Rollback
     INSERT INTO Auditoria (Fecha)
     VALUES (GETDATE())
  
     ROLLBACK TRAN B
    -- Verificamos que Trancount es 2 y no 0
    -- Esto se da porque el rollback afecta 
    -- el punto de almacenamiento y no la transacción
    -- en si
     SELECT @@TRANCOUNT   

-- Hacemos los Commit
 COMMIT TRAN  
 COMMIT TRAN  
-- Verificamos que Trancount queda en 0
SELECT @@TRANCOUNT   
go    

-- Verificamos los datos y vemos que en Auditoria
-- no se guardaron registros porque hubo un rollback
-- pero solo de ese punto

SELECT * FROM Cabecera 
SELECT * FROM Detalle 
SELECT * FROM Auditoria 
  

Control de errores y transacciones

Hasta aquí hemos visto que el manejo del Rollback es muy distinto al del Commit Tran, ahora bien, como hacemos para controlar los errores ? la buena practica es que ante un error lo primero que se haga sea un Rollback pero que sucede si hubo un rollback anterior y el TranCount quedo en 0? Lo que deberíamos hacer es en control del error verificar primero la variable Trancount y si es mayor que 0 entonces si hacer el Rollback, aquí vemos un ejemplo.

 

-- ======================================= 
--      Manejo de Errores & transacciones 
-- =======================================
  
SELECT @@TRANCOUNT 

-- Abrimos una transacción
BEGIN TRY

BEGIN TRAN
  -- hacemos los insert
  INSERT INTO Cabecera 
  VALUES (1,GETDATE())
  
  INSERT INTO Detalle 
  Values (1,1,100)
  
  -- Abrimos otra transacción
  BEGIN TRAN 
    -- Verificamos que Trancount se incremento en 1
    SELECT @@TRANCOUNT   
    
    -- Generamos un error para ir al bloque CATCH
    INSERT INTO Auditoria (Fecha)
    VALUES (NULL)
  
    -- Verificamos que Trancount quedo en 0
    -- Lo cual ha hecho un rollback de todo
  
END TRY

-- Control de errores

BEGIN CATCH
  IF @@TRANCOUNT > 0
   BEGIN
    ROLLBACK TRAN
   END 

SELECT ERROR_MESSAGE() 

END CATCH
 

Conclusiones

En este post les he mostrado como se manejan y como funcionan las transacciones en SQL Server, siempre recuerden que las transacciones deberían durar lo mínimo posible para evitar loqueos que terminen en problemas de performance, escriban solo el código adecuado dentro del Begin y Commit.

Esto que vimos también se aplica si hay Stores procedures y en uno abren una transacción que llama a otro que a su vez abre otra transacción, recuerden del save tran para esos manejos si lo que desean es volver a un punto determinado.

Currently rated 4.1 by 9 people

  • Currently 4.111111/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To | TSQL
Posted by maccotto on Friday, June 19, 2009 12:51 AM
Permalink | Comments (18) | Post RSSRSS comment feed

Como saber la versión y service pack de nuestro SQL

Hay muchas veces que se necesita saber que edición, versión y service pack tenemos en nuestra instalación.

Para poder sacar esta información hay distintas formas, una de ellas es usar la siguiente query

 

SELECT @@VERSION 
 

Obteniendo como resultado el siguiente ejemplo

 

Microsoft SQL Server 2008 (SP1) - 10.0.2531.0 (Intel X86)
    Mar 29 2009 10:27:29
    Copyright (c) 1988-2008 Microsoft Corporation
    Developer Edition on Windows NT 6.0 <X86> (Build 6002: Service Pack 2)

 
Aquí podemos observar el tipo de edición (Developer) si es 32 o 64 bits, el nivel de Service pack del SO (SP2)
el build del SQL Server (10.0.2531.0) , su versión (SQL 2008) y hasta su service pack (SP1).
 
También podemos hacer esto mismo y además obtener mas información utilizando la función ServerProperty.
 
select SERVERPROPERTY('Edition') as Edicion,
       SERVERPROPERTY('ProductLevel') as ServicePack,
       
       case when
       
       substring(convert(varchar(15),
                 SERVERPROPERTY('ProductVersion')),
                 0,       
                 patindex('%.%',
                 convert(varchar(15),
                 SERVERPROPERTY('ProductVersion')))) = 8
       then '2000'           
       when 
       substring(convert(varchar(15),
                 SERVERPROPERTY('ProductVersion')),
                 0,       
                 patindex('%.%',
                 convert(varchar(15),
                 SERVERPROPERTY('ProductVersion')))) = 9
       then '2005'
       when 
       substring(convert(varchar(15),
                 SERVERPROPERTY('ProductVersion')),
                 0,       
                 patindex('%.%',
                 convert(varchar(15),
                 SERVERPROPERTY('ProductVersion')))) = 10
       then '2008' end as version,
       SERVERPROPERTY('Collation') as Collation,
       SERVERPROPERTY('InstanceName') as instancia
 
 

Currently rated 4.7 by 3 people

  • Currently 4.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Sunday, June 07, 2009 1:59 AM
Permalink | Comments (8) | Post RSSRSS comment feed

Como pasar una base de SQL 2008 a 2005 o 2000

En algunas ocasiones podemos necesitar transformar una base de datos completa de una versión superior a una inferior.

Si  en la base de datos por ejemplo 2008 no se le han implementado features exclusivas de esa versión la conversión debería ser mas natural y simple que si tenemos features nuevas (lo cual deberíamos hacer algún proceso de reingeniería)

Supongamos que tenemos una base en un SQL 2008 a la cual no le aplicamos nuevas funcionalidades y se nos da por pasarla hacia atrás, uno lo primero que intentaría hacer es realizar un backup y luego un restore por ejemplo en el 2000 o 2005, si hacen esto recibirán un error al hacer el restore (Error 3169: The Backed-up database has on-disk structure version ….)

, este error se debe a que al estar en una version superior el formato de archivos es distinto por mas que la base este en un modo de compatibilidad inferior, supongamos que esta base de datos esta en un 2008 con un modo de compatibilidad 2000.

Entonces como hacemos esta tarea?

Pues bien, hay varias técnicas, una de ellas es armar primero los Script DDL de la base (tablas, índices, vistas, Stores, etc.) y luego por un método de exportación e importación de datos pasar la data.

Este método requiere de varios pasos y además de cierta complejidad.

En este post veremos como hacer esto mismo con un solo Script usando el Management Studio 2008.

 

  • Entre a su SSMS (SQL Server Management Studio) de 2008
  • Seleccione la base de datos que quiere pasar
  • Pulse botón alterno del mouse sobre ella, vaya a Task (o tareas) y luego a Generate Script.(Al hacer esto le aparecerá el siguiente Wizard).

image

  • Pulse el Checkbox "Script all Object in the selected database”, esto nos indica que se hará script de todos los objetos de esa base de datos y pulse el botón Next
  • En la siguiente pantalla observara las distintas opciones para generar el Script, y es aquí donde debemos concentrarnos aun mas y poner lo siguiente en cada opción
      • Script for SQL Server Version = SQL 2000 0 2005 dependiendo a que version queremos ir
      • Script Data = True
      • Script Foreign Key = True
      • Script Indexes = True
      • Script Primary Keys = True
      • Script Triggers  = True
      • Script Unique Key = True
      • Script Database Create = True

Con estas opciones de mínima ya estamos en condiciones de pasar al próximo paso del asistente en el cual indicaremos donde queremos guardar el script, por ejemplo yo seleccionare hacerlo en el disco C y con el nombre de Script2000.SQL.

 

image

Luego de hacer esto debemos culminar con el asistente el cual generara los Script con los datos incluidos para la version de SQL que le hemos seleccionado.

Si no se encuentra alguna compatibilidad, por ejemplo en la base tenemos cosas propias de 2008 el asistente no las convertirá y marcara un error en el paso indicando donde tenemos el problema, de sucedernos esto deberíamos hacer una reingeniería, por ejemplo cambiar el tipo de dato.

En mi caso se genero correctamente el Script ya que la base de datos que tenia en 2008 era un restore de una de 2000 pero luego no la podía pasar a 2000 con Backup y restore por el cambios de formato comentado en el inicio del post.

Bueno ahora lo que nos queda es abrir nuestro Query Analizer y ejecutar el Script  y tendremos creada en 2000 nuevamente la base con sus estructuras y datos también.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: How To
Posted by maccotto on Monday, June 01, 2009 10:24 AM
Permalink | Comments (4) | Post RSSRSS comment feed