Sunteți pe pagina 1din 10

1 de 10

Traducido al: español Mostrar texto original Opciones ▼

34 1

34

He pasado muchos años tratando de convencer a


la gente a usar declarativa en lugar de código de
procedimientos en SQL. Uno de mis libros está
pensando en sets, con el subtítulo de gran
tamaño “Auxiliar, temporal y tablas virtuales en
SQL” para explicar qué temas están cubiertos. La
mayoría de los programadores descubren que es
demasiado grande un gran salto desde una
mentalidad de procedimiento para una
mentalidad declarativa, y por lo tanto no llegan a
hacer la transición a la vez. En su lugar,
evolucionan a partir de un paradigma
procedimental a una variedad de estilos de
2 de 10

Esta es sólo la forma en que se aprende; un paso


a la vez, no es un salto de la visión a la vez. Las
primeras imágenes en movimiento, por ejemplo,
fueron tomadas con una cámara fija posición
dirigida a un escenario. Así es como la gente
había visto obras de teatro para varios miles de
años. WD Griffith fue a películas como Dr. Codd
fue a bases de datos. Griffith hizo el primero de
dos reeler en 1910; nadie había pensado en más
de un carrete antes de eso. Luego, en 1911 y 1912
él y su camarógrafo, Billy Bitzer, empezó a mover
la cámara y la lente durante el rodaje. Él nos dio
nuevos ángulos de cámara, como el primer plano
y el enfoque suave.

En 2010 Febrero, me encontré con un ejemplo, en


una discusión de grupos de noticias, de los
programadores que hacen los pasos, pero no el
salto. Los detalles de los códigos no son
relevantes para el punto que estoy haciendo así
que voy a pasar por encima de ellos.

El hilo comienza con un anuncio acerca de una


función definida por el usuario (UDF) que no está
funcionando. Su párrafo inicial fue:

“Tengo el siguiente código para tomar un


conjunto de valores enteros y devolver un
VARCHAR en base a la combinación de entradas.
Sin embargo, estoy recibiendo un error en la línea
6, que es la primera línea donde la palabra CASO
aparece. Un montón de declaraciones de casos
han pasado por mis manos antes, así que estoy
perdido de por qué éste es un error.”

Lo que siguió fue una expresión CASE con los


intermediarios y RUP y construcciones caso
dentro de CASE. Tomó pares de (x, y) y produjo
una respuesta de un conjunto de tres valores,
3 de 10

codificación no son mi punto. El cuerpo de la


función podría muy bien haber sido una expresión
matemática complicada.

Dos respondedores señalaron que CASE es una


expresión y no una declaración en SQL. También
señalaron que regresaba de un VARCHAR (1) en
lugar de un CHAR (1). La función CASE puede ser
confuso para cualquiera que haya sido
inicialmente entrenado con un lenguaje de
procedimientos que tiene construcciones
instrucción IF-THEN-ELSE.

Su código se parecía a este esqueleto:

CREATE FUNCTION FnFindFoobar (@x INTEGER


RETURNS VARCHAR
WITH EXECUTE AS CALLER
AS
BEGIN
<< horrible CASE expression with x and
END;

La solución rápida y limpia era:

CREATE FUNCTION Find_Foobar (@in_x INTEGER


RETURNS CHAR(1)
WITH EXECUTE AS CALLER
AS
BEGIN
RETURN (<< horrible CASE expression with
END;

luego le preguntó a otra persona si había


considerado previamente el cálculo de los
resultados de la expresión CASE y llenar una
mesa con ellos. Este era un buen consejo, ya que
el número de (x, y) pares implicados llegó a unos
pocos miles de casos. No hay punto de despedir
a esta solución cuando la tabla de consulta es tan
pequeño como éste. De sólo lectura tablas de
este tamaño tienden a estar en el
4 de 10

que puedan compartirse entre muchas sesiones,


y que no van a ahorrar mucho en la memoria por
la elección de un método diferente.

Pero la persona que hizo esta sugerencia pasó a


añadir “Se puede utilizar la tabla con su UDF o
usted podría utilizarlo sin la UDF.”, Pero no explicó
cuáles son las diferencias. Son importantes.
Poner los datos en las tablas de sólo lectura este
tamaño tenderá a mantener el mismo
almacenado o caché principal, Si usted es
realmente tan apretada para el almacenamiento
primario y / o secundario que no puede adaptarse
a una fila de la tabla ~ 5K en su hardware,
comprar un poco fichas y discos. Son tan barato
hoy en día. Ahora los datos se comparte entre
muchas sesiones. La tabla y sus índices pueden
ser utilizados por el optimizador. En SQL Server,
puede incluir la columna sola foobar en el índice
para obtener un índice de cobertura y mejora el
rendimiento.

Pero si decide bloquear los datos dentro del


código procesal de una UDF, que puede ser
compartida? ¿Los cálculos se repiten con cada
invocación? ¿Qué pasa con índices? ¡Ay! Una UDF
bastante-mucho cerraduras cosas dentro.
Estándar SQL / PSM tiene una [NO] opción
DETERMINISTIC en sus declaraciones de
procedimiento. Esto le dice al compilador si el
procedimiento o función siempre va a devolver la
misma respuesta para los mismos argumentos.
5 de 10

 tener en cuenta sobre términos SQL


estándar : un parámetro es el titular
legítimo lugar en la lista de parámetros de
una declaración y un argumento es el
valor pasado en la invocación del
procedimiento

 Una función no determinista tiene que ser


calculada una y otra vez, cada vez que el UDF se
llama; si el optimizador de consultas no sabe con
certeza si el procedimiento de AA o función
determinista, tiene que asumir que no es e ir a la
ruta larga.

Aquí está el esqueleto de lo que fue publicado.

-- Create table
CREATE TABLE Foobar
(x INTEGER NOT NULL,
y INTEGER NOT NULL,
foobar CHAR(1) NOT NULL,
PRIMARY KEY CLUSTERED (x, y));
-- Populate table with recursive CTEs and proprietary
WITH X_CTE
AS
(SELECT 100 AS x
UNION ALL
SELECT x + 1
FROM X_CTE
WHERE x < 300),
Y_CTE
AS
(SELECT 1 AS y
UNION ALL
SELECT y + 1
FROM Y_CTE
WHERE y < 100)
INSERT INTO Foobar (x, y, foobar)
SELECT x, y, << horrible CASE expression
FROM X_CTE
CROSS JOIN
Y_CTE;

Este es un buen truco, pero vale la pena señalar


que es muy exclusivo. Ningún otro SQL ha
6 de 10

requiere una palabra clave RECURSIVO, y no se


puede utilizar con cualquier declaración CTE, pero
un SELECT. Es bastante fácil de volver a escribir
esto en portátiles estándar SQL, utilizando una
tabla de números enteros llamados Serie (solía
llamarlo secuencia, pero que ahora es una
palabra reservada en SQL estándar. Si usted tiene
un nombre mejor, por favor dígame ). Esta es la
tabla auxiliar de SQL más común; programadores
experimentados crean SQL y luego una mesa
Calendario en el inicio de casi nuevo proyecto.

INSERT INTO FooLookup (x, y, foobar)


SELECT X_CTE.x, Y_CTE.y, << horrible
FROM (SELECT seq
FROM Series
WHERE seq BETWEEN 100 AND 300) AS X_CTE
CROSS JOIN
(SELECT seq
FROM Series
WHERE seq BETWEEN 1 AND 100) AS Y_CTE

Recursividad, una herramienta procesal, es caro.


Pero eso no es mi punto. La primera idea era
utilizar una herramienta procesal y no un enfoque
impulsado por los datos para conseguir ese
CROSS JOIN. Lo que quiero decir un modo de
pensar? Este es el tipo semi-procedimiento que
se remonta a lo que sabe. Casi llegó a un modo
de pensar declarativa.

Ahora vamos a ir adelante con el resto del


esqueleto de código para la función:

CREATE FUNCTION Find_Foobar


(@in_x INTEGER, @in_y INTEGER)
RETURNS CHAR(1)
WITH EXECUTE AS CALLER
AS
BEGIN
RETURN
COALESCE
((SELECT foobar
FROM Find_Foobar
7 de 10

AND y = @in_y), 'A');


END;

La razón de COALESCE () es que 'A' es un valor


por defecto en la expresión caja exterior, sino
también un resultado válido en diversas cláusulas
entonces y más dentro de expresiones caja
interior. La consulta escalar devuelve un NULL si
no puede encontrar un (, Y, foobar x) fila de la
tabla. Si sabemos que la consulta cubre la
totalidad (x, y) universo, entonces nosotros no
necesitamos el COALESCE () y podría haber
evitado una UDF por completo.

Ahora, vamos a pensar acerca de la programación


declarativa. En SQL que significa restricciones en
la declaración de tabla en el DDL. Este esqueleto
tiene ninguno, excepto la clave principal. Aiee!
Aquí es un problema que se encuentra con
artículos de revistas y grupos de noticias; Es tan
fácil de pasar por alto las limitaciones cuando se
proporciona una tabla esqueleto. Usted no los
necesita cuando usted declaró un archivo,
¿verdad? Lo que se puede olvidar es que los sub-
idiomas de tres SQL (DDL, DML y DCL) trabajan
juntos. En particular, las restricciones DDL son
utilizados por el optimizador de LMD para
proporcionar una mejor estrategia de ejecución ..

Las << horrible CASE expression


>>expectativas implícitas de x e y. Nos dieron
límites inferiores (100 y 1), pero los límites
superiores estaban abiertos después de una
pequeña gama de (x, y) pares. Creo que podemos
asumir que el cartel original espera que la gran
mayoría de los casos (o todos ellos) a caer en ese
rango pequeño y quería manejar cualquier otra
cosa como un error. En el mundo real, por lo
general hay lo que Jerry Weinberg llama
8 de 10

principio es también conocida como la ley de Zipf


o el principio de “buscar un caballo y una cebra
no” en la medicina.

El primer disparo sencillo sería suponer que


siempre conocemos los límites y sólo se puede
utilizar:

CREATE TABLE FooLookup


(x INTEGER NOT NULL
CHECK (x BETWEEN 100 AND 300),
y INTEGER NOT NULL
CHECK (y BETWEEN 1 AND 100),
foobar CHAR(1)
DEFAULT 'A'
NOT NULL
CHECK (foobar) IN ('A', 'B', 'C'),
PRIMARY KEY (x, y));

El valor por defecto 'A' subcláusula se encargará


de situación en la que no tienen un valor explícito
para foobar. Esto evita la COALESCE (). Pero lo
que si uno de los parámetros puede ser cualquier
cosa? Eso es fácil; dejar caer la verificación () y
comentarlo. ¿Qué pasa si uno de los parámetros
es un medio abierto o tiene un espacio enorme,
pero escasa? Es decir, sabemos que un límite
inferior (superior), pero no el límite de juego
superior (inferior). Sólo tiene que utilizar una
comparación simple, tal como CHECK (y> = 1), en
lugar de un medio.

Una situación común, que se hizo con la


expresión CASE anidado en el original, es que
sabes un rango de un parámetro y lo que los
resultados para el otro parámetro dentro de ese
rango son. Eso podría ser más fácil de ver con el
código. Aquí es una expresión CASE para algunos
de los posibles (x, y) pares:

CASE
WHEN x BETWEEN 100 AND 200
9 de 10

WHEN y IN (2, 4, 6, 8) THEN 'B'


WHEN y IN (1, 3, 5, 7, 9) THEN 'C'
END
WHEN x BETWEEN 201 AND 300
THEN CASE
WHEN y IN (2, 4, 6, 8, 99) THEN 'C'
WHEN y IN (3, 5, 7, 9, 100) THEN 'B'
END
ELSE 'A'
END

Esta es la versión LMD de una restricción. Vive


sólo en la inserción. UPDATE, INSERT o SELECT
en el que aparece. Lo que realmente queremos es
limitaciones en el DDL de modo que todas las
declaraciones, presente y futuro, lo utilizan. El
truco es crear la tabla con valores bajos y altos
para cada rango de parámetro; un único valor se
muestra con los valores alto y bajo iguales entre
sí.

CREATE TABLE FooLookup


(low_x INTEGER NOT NULL,
high_x INTEGER NOT NULL,
CHECK (low_x <= high_x),
low_y INTEGER NOT NULL,
high_y INTEGER NOT NULL,
CHECK (low_y <= high_y),
foobar CHAR(1) NOT NULL
CHECK (foobar) IN ('A', 'B', 'C'),
PRIMARY KEY (x, y));

expresión CASE se convierte ahora en esta tabla:

low_x high_x low_y high_y foobar


==================================
100 200 2 2 'B'
100 200 6 6 'B'
100 200 8 8 'B'
100 200 1 1 'C'
100 200 3 3 'C'
100 200 5 5 'C'
100 200 7 7 'C'
100 200 9 9 'C'
201 300 2 2 'C'
201 300 4 4 'C'
201 300 6 6 'C'
201 300 8 8 'C'
10 de 10

201 300 3 3 'B'


201 300 5 5 'B'
201 300 7 7 'B'
201 300 9 9 'B'
201 300 100 100 'B'
301 9999 101 9999 'A'
-9999 99 -9999 0 'A'

Como dispositivo de seguridad, poner el valor por


defecto 'A' en rangos fuera del resto de la mesa.
Solía -9999 y 9999 para los límites de menor y
mayor, pero se entiende la idea.

La consulta tiene que utilizar intermediarios en


los límites superior e inferior:

SELECT F.foobar, ..
FROM FooLookup AS F, ..
WHERE my_x BETWEEN F.low_x AND F.high_x
AND my_y BETWEEN F.low_y AND F.high_y
AND ..;

Está presente siempre va a ser la mejor manera


de hacer algo? ¿Quién sabe? Pruébalo.

S-ar putea să vă placă și