Recuperar query de SQL Server cuando cerramos un tab por error

Hoy estuve casi todo el día realizando un query para calcular el Kardex de un producto cuando en un momento de emoción estúpida cerré el tab del query que estába desarrollando. Googleando un poco encontré el siguiente query el cual muestra todos los querys ejecutados, lo único que si, te lo da en una sola línea, pero dar unos cuantos enters es mucho mejor que rehacer todo lo hecho. SELECT execquery.last_execution_time AS [Date Time], execsql.text AS [Script] FROM sys.dm_exec_query_stats AS execquery CROSS APPLY sys.dm_exec_sql_text(execquery.sql_handle) AS execsql ORDER BY execquery.last_execution_time DESC

Obtener todos los permisos de un usuario SQL Server

Algunas veces (por temas de auditoría) es necesario saber cuáles son todos los permisos que se tienen dentro de una BD.

No he encontrado un script que me permita saber absolutamente todos los permisos que posee mi usuario (teniendo en cuenta que el permiso puede no estar asociado a mi directamente si no a mi rol), es por eso que elaboré éste script que cubre en parte ésta necesidad.

El script que se muestra a continuación obtiene todos los permisos que tiene nuestro Login a nivel de servidor y además todos los permisos que tiene el usuario relacionado a nuestro login en cada Base de Datos.

Indiqué que esto cubría la necesidad "en parte" debido a que un permiso puede ser asignado directamente hacia un objeto lo cual está fuera del alcance del presente Script.

De todas maneras trabajaré una versión un poco más completa que cubra totalmente lo que se busca.

El Script es el siguiente:

SELECT 'Login: ' + SYSTEM_USER AS Login_Usuario
 
SELECT permission_name AS 'Permisos a nivel de servidor:'
FROM fn_my_permissions(NULL, 'SERVER')
 
DECLARE @Texto AS NVARCHAR(MAX)
 
SET @Texto = '
USE [?]
IF((SELECT COUNT(1) FROM sys.database_principals WHERE name = CURRENT_USER)>0)
BEGIN
      BEGIN TRY
            SELECT permission_name AS ''Permisos a nivel de BD (?):''
            FROM fn_my_permissions(NULL, ''DATABASE'')
      END TRY
      BEGIN CATCH
            SELECT ''Ocurrió un error: '' + ERROR_MESSAGE() AS ''Permisos a nivel de BD (?):''
      END CATCH
END'
 
EXEC master.sys.sp_MSforeachdb @Texto

Consultas SQL más costosas

Algunas veces, sobre todo cuando tenemos entre manos un proceso de optimización, es necesario saber exactamente cuáles son las sentencias que tienen un costo o tiempo de ejecución mayor para poder enfocar nuestras fuerzas en ése punto.

Para suerte nuestra, en SQL se guarda un histórico de todas las veces que se ejecuta una sentencia, por lo que podemos consultar no sólo cuantas veces se ha ejecutado (en el caso de los procedimientos, la cantidad de veces se cuenta desde su última compilación) si no que podemos ver el tiempo y costo (de CPU) promedio de todas las ejecuciones, el tiempo y costo de la última ejecución entre otros datos más.

SELECT TOP 10
    DB_NAME(qt.dbid) 'Base de Datos',
    OBJECT_NAME(qt.objectid,qt.dbid)AS 'Nombre Objeto',
    SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
    ((CASE qs.statement_end_offset
    WHEN -1 THEN DATALENGTH(qt.text)
    ELSE qs.statement_end_offset
    END - qs.statement_start_offset)/2)+1) AS 'Texto',
    qs.execution_count AS 'Veces ejecutado',
    qs.total_logical_reads AS 'Total lecturas lógicas',
    qs.last_logical_reads AS 'Lecturas lógicas del último proceso',
    qs.total_logical_writes AS 'Total escrituras lógicas',
    qs.last_logical_writes AS 'Escrituras lógicas del último proces',
    qs.total_worker_time AS 'Total tiempo CPU',
    qs.last_worker_time AS 'Tiempo CPU del último proceso',
    qs.min_worker_time AS 'Minimo tiempo CPU',
    qs.max_worker_time AS 'Maximo tiempo CPU',
    qs.total_elapsed_time/1000000 AS 'Total tiempo (en seg)',
    qs.last_elapsed_time/1000000 AS 'Tiempo del último proceso (en seg)',
    qs.min_elapsed_time/1000000 AS 'Tiempo mínimo (en seg)',
    qs.max_elapsed_time/1000000 AS 'Tiempo máximo (en seg)',
    qs.last_execution_time AS 'Ultima vez que se ejecutó',
    qp.query_plan AS 'Plan de ejecución'
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
--WHERE DB_NAME(qt.dbid) = 'NOMBRE_DE_BD'
ORDER BY qs.total_elapsed_time DESC

Siempre pueden listar más items (quitando el TOP 10), ordenar el resultado por otro factor (cambiando el ORDER BY) o restringiendo la búsqueda a una base de datos específica (descomentando la penúltima línea y cambiando NOMBRE_DE_BD por el nombre de su Base de Datos)

Mantener scroll luego de un postback (incluso de UpdatePannel)

Estuve trabajando con un treeview el cual estaba dentro de un div (que tenía scrolls) y los cuales estan dentro de un UpdatePannel y por cosas del destino tuve que hacer que al pulsar el boton "+" del TreeView ésto me genere un postback dentro del UpdatePannel.

Hasta aquí todo perfecto pero sucedía que como había un postback, los scrolls del div se seteaban a su estado original (arriba y a la derecha) cada vez que hacía clic en el botón "+".

Buscando un poco encontré la solución aquí.

Y resultó ser tan sencillo como agregar éste script LUEGO de cualquier ScriptManager que pueda existir (recomiendo agregarlo al final, justo antes del </body>):

<script type="text/javascript">
 var xPos, yPos;
 var prm = Sys.WebForms.PageRequestManager.getInstance();
 prm.add_beginRequest(BeginRequestHandler);
 prm.add_endRequest(EndRequestHandler);
 function BeginRequestHandler(sender, args) {
  xPos = document.getElementById('divScrolls').scrollLeft;
  yPos = document.getElementById('divScrolls').scrollTop;
 }
 function EndRequestHandler(sender, args) {
  document.getElementById('divScrolls').scrollLeft = xPos;
  document.getElementById('divScrolls').scrollTop = yPos;
 }
</script>

Nota anti dummies: es necesario cambiar el valor de "divScrolls" por el id del control con el que vayan a trabar.

Sólo para explicarlo muy genéricamente, lo que se hace es generar eventos en la página (BeginRequestHandler y EndRequestHandler) para que antes de ejecutar un PostBack se guarden los valores del scroll y luego del PostBack se seteen los valores guardados previamente.

La funcionalidad también se puede trabajar con Jquery reemplazando los
document.getElementById('
por
$('#

Depende de los gustos de cada uno =)

Buenas prácticas en procedimientos almacenados (SQL Server 2005)

Por un encargo, estoy revisando algunas buenas prácticas que recomiendan nuestros amigos de Microsoft, algunas ya las conocía pero otras me resultan bastante interesantes (dado que cometo esos errores muy a menudo XD).
Considero importante recalcar que estas recomendaciones están "optimizadas" para SQL 2005 pero muchas de ellas (por un tema lógico) pueden ser usadas también para las versiones posteriores de éste motor de BD.
Considero importante también que no haré este manual muy "newbie", por lo que si se tiene alguna pregunta (y sé la respuesta) lo conversaremos por los mensajes al post.

Liberar memoria con VB.Net

A pesar de que es recomendado dejar que el Garbage Collector (Recolector de basura para los amantes del español) haga el trabajo "sucio" dentro de un aplicativo VB, muchas veces éste parece estar de huelga o algo por el estilo, pero resulta haciendo una porquería de trabajo.
Si estas al borde de la desesperación, imagino que este código podría ayudarte un poco.

'Declaración de la API
Private Declare Auto Function SetProcessWorkingSetSize Lib “kernel32.dll” (ByVal procHandle As IntPtr, ByVal min As Int32, ByVal max As Int32) As Boolean
'Funcion de liberacion de memoria
Public Sub ClearMemory()
   Try
       Dim Mem As Process
       Mem = Process.GetCurrentProcess() SetProcessWorkingSetSize(Mem.Handle, -1, -1)

   Catch ex As Exception
   'Control de errores
   End Try
End Sub


Fuente: http://gdev.wordpress.com/2005/11/30/liberar-memoria-con-vb-net/

Probar procedures en SQL

Ésta entrada será muy corta.

Hace algún tiempo me dijeron "tienes que optimizar este procedure", así que lo analicé, hice algunas mejoras, y en la primera corrida del procedure, demoró aproximadamente 3 veces más de lo que demoraba el procedure original, pensé que había un error dado a que había aplicado (según yo) las "buenas prácticas", así que lo ejecuté denuvo y demoró la mitad del procedure original.

Que pasó? Sencillo, el caché y el buffer.

Tengan en cuenta que la ventaja de un procedure, es que compila la consulta y la guarda compilada, (es por eso que es una muy buena práctica el usuarlos) y lo que pasó fue que en la primera ejecución de mi modificación, es procedure tuvo que eliminarse y volverse a compilar.

Buscando un poco con internet, di con un método que me ayudaría un poco con esto de los tiempos (dejando de lado el usar el analizador de querys obviamente) y es el hecho de simplemente limpiar el caché y el buffer antes de ejecutar el procedure y esto se hace con los siguientes métodos

DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE


Saludos