sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Cómo pasar un conjunto de filas de una función a otra?

Funciones de tabla

Realizo migraciones de bases de datos complejas y de muy alta velocidad para ganarme la vida, usando SQL como lenguaje del cliente y del servidor (no se usa ningún otro lenguaje), todo ejecutándose en el lado del servidor, donde el código rara vez aparece desde el motor de la base de datos. Las funciones de tabla juegan un papel ENORME en mi trabajo . No uso "cursores" ya que son demasiado lentos para cumplir con mis requisitos de rendimiento, y todo lo que hago está orientado a resultados. Las funciones de tabla han sido de gran ayuda para mí al eliminar por completo el uso de cursores, lograr una velocidad muy alta y han contribuido dramáticamente a reducir el volumen de código y mejorar la simplicidad.

En resumen, utiliza una consulta que hace referencia a dos (o más) funciones de tabla para pasar los datos de una función de tabla a la siguiente. El conjunto de resultados de la consulta de selección que llama a las funciones de tabla sirve como conducto para pasar los datos de una función de tabla a la siguiente. En la plataforma/versión de DB2 en la que trabajo, y según parece, según un vistazo rápido al manual 9.1 de Postgres, lo mismo es cierto allí, solo puede pasar una sola fila de valores de columna como entrada a cualquiera de las llamadas de función de tabla, como has descubierto. Sin embargo, debido a que la llamada a la función de tabla ocurre en medio del procesamiento del conjunto de resultados de una consulta, se logra el mismo efecto de pasar un conjunto de resultados completo a cada llamada a la función de tabla, aunque, en las tuberías del motor de la base de datos, los datos se pasan solo una fila a la vez para cada función de tabla.

Las funciones de tabla aceptan una fila de columnas de entrada y devuelven un único conjunto de resultados en la consulta de llamada (es decir, seleccionar) que llamó a la función. Las columnas del conjunto de resultados devueltas desde una función de tabla pasan a formar parte del conjunto de resultados de la consulta de llamada y, por lo tanto, están disponibles como entrada para la siguiente función de tabla , al que se hace referencia más adelante en la misma consulta, normalmente como una unión posterior. Las columnas de resultados de la primera función de tabla se envían como entrada (una fila a la vez) a la segunda función de tabla, que devuelve sus columnas de conjunto de resultados al conjunto de resultados de la consulta de llamada. Tanto la primera como la segunda columna del conjunto de resultados de la función de tabla ahora forman parte del conjunto de resultados de la consulta de llamada y ahora están disponibles como entrada (una fila a la vez) para una tercera función de tabla. Cada llamada de función de tabla amplía el conjunto de resultados de la consulta de llamada a través de las columnas que devuelve. Esto puede continuar hasta que comience a alcanzar los límites en el ancho de un conjunto de resultados, que probablemente varíe de un motor de base de datos a otro.

Considere este ejemplo (que puede no coincidir con los requisitos o capacidades de sintaxis de Postgres mientras trabajo en DB2). Este es uno de los muchos patrones de diseño en los que uso funciones de tabla, es uno de los más simples, creo que es muy ilustrativo y anticipo que tendría un gran atractivo si Las funciones de tabla tenían un uso generalizado intensivo (que yo sepa, no lo son, pero creo que merecen más atención de la que están recibiendo).

En este ejemplo, las funciones de tabla en uso son:VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH y DATA_WAREHOUSE_TODAYS_ORDER_BATCH. En la versión de DB2 en la que trabajo, envuelve la función de tabla dentro de "TABLE (coloque la llamada de función de tabla y los parámetros aquí)", pero según un vistazo rápido a un manual de Postgres, parece que omite el contenedor "TABLE ()".

create table TODAYS_ORDER_PROCESSING_EXCEPTIONS as (

select      TODAYS_ORDER_BATCH.*
           ,VALIDATION_RESULT.ROW_VALID
           ,POST_RESULT.ROW_POSTED
           ,WAREHOUSE_RESULT.ROW_WAREHOUSED

from        TODAYS_ORDER_BATCH

cross join  VALIDATE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function]  ) 
              as VALIDATION_RESULT ( ROW_VALID )  --example: 1/0 true/false Boolean returned

left join   POST_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
              as POST_RESULT ( ROW_POSTED )  --example: 1/0 true/false Boolean returned
      on    ROW_VALIDATED = '1'

left join   DATA_WAREHOUSE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
              as WAREHOUSE_RESULT ( ROW_WAREHOUSED )  --example: 1/0 true/false Boolean returned
      on    ROW_POSTED = '1'

where       coalesce( ROW_VALID,      '0' ) = '0'   --Capture only exceptions and unprocessed work.  
      or    coalesce( ROW_POSTED,     '0' ) = '0'   --Or, you can flip the logic to capture only successful rows.
      or    coalesce( ROW_WAREHOUSED, '0' ) = '0'

) with data
  1. Si la tabla TODAYS_ORDER_BATCH contiene 1 000 000 de filas, se llamará a VALIDATE_TODAYS_ORDER_BATCH 1 000 000 veces, una vez por cada fila.
  2. Si 900 000 filas pasan la validación dentro de VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH se llamará 900 000 veces.
  3. Si solo se publican correctamente 850 000 filas, entonces VALIDATE_TODAYS_ORDER_BATCH necesita cerrar algunas lagunas LOL, y se llamará a DATA_WAREHOUSE_TODAYS_ORDER_BATCH 850 000 veces.
  4. Si 850 000 filas ingresaron con éxito en el almacén de datos (es decir, no se generaron excepciones adicionales), la tabla TODAYS_ORDER_PROCESSING_EXCEPTIONS se completará con 1 000 000 - 850 000 =150 000 filas de excepción.

Las llamadas a la función de tabla en este ejemplo solo devuelven una sola columna, pero podrían devolver muchas columnas. Por ejemplo, la función de tabla que valida la fila de un pedido podría devolver el motivo por el cual un pedido falló en la validación.

En este diseño, se elimina prácticamente toda la charla entre un HLL y la base de datos, ya que el solicitante de HLL le pide a la base de datos que procese todo el lote en UNA solicitud. Esto da como resultado una reducción de millones de solicitudes SQL a la base de datos, una ENORME eliminación de millones de procedimientos HLL o llamadas a métodos y, como resultado, proporciona una ENORME mejora del tiempo de ejecución. Por el contrario, el código heredado que a menudo procesa una sola fila a la vez, normalmente enviaría 1 000 000 de solicitudes SQL de búsqueda, 1 para cada fila en TODAYS_ORDER_BATCH, más al menos 1 000 000 de solicitudes HLL y/o SQL con fines de validación, más al menos 1 000 000 de HLL y /o solicitudes SQL para fines de publicación, más 1.000.000 de solicitudes HLL y/o SQL para enviar la orden al almacén de datos. De acuerdo, utilizando este diseño de función de tabla, dentro de las funciones de tabla, las solicitudes SQL se envían a la base de datos, pero cuando la base de datos se solicita a sí misma (es decir, desde dentro de una función de tabla), las solicitudes SQL se atienden mucho más rápido (especialmente en comparación con un escenario heredado en el que el solicitante de HLL está realizando un procesamiento de una sola fila desde un sistema remoto, con el peor de los casos en una WAN:Dios mío, no haga eso).

Puede encontrarse fácilmente con problemas de rendimiento si utiliza una función de tabla para "obtener un conjunto de resultados" y luego unir ese conjunto de resultados a otras tablas. En ese caso, el optimizador de SQL no puede predecir qué conjunto de filas se devolverá de la función de tabla y, por lo tanto, no puede optimizar la unión a tablas posteriores. Por esa razón, rara vez los uso para obtener un conjunto de resultados, a menos que sepa que el conjunto de resultados será un número muy pequeño de filas, por lo tanto, no causará un problema de rendimiento, o no necesito unirme a tablas posteriores.

En mi opinión, una de las razones por las que las funciones de tabla están infrautilizadas es que a menudo se las percibe solo como una herramienta para obtener un conjunto de resultados, que a menudo funciona mal, por lo que se descartan como una herramienta "pobre" para usar.

Las funciones de tabla son inmensamente útiles para enviar más funciones al servidor, para eliminar la mayor parte de las conversaciones entre el servidor de la base de datos y los programas en sistemas remotos, e incluso para eliminar las conversaciones entre el servidor de la base de datos y los programas externos en el mismo servidor. Incluso las conversaciones entre programas en el mismo servidor conllevan más gastos generales de lo que mucha gente cree, y gran parte de ellos son innecesarios. El corazón del poder de las funciones de tabla radica en usarlas para realizar acciones dentro del procesamiento de conjuntos de resultados.

Hay patrones de diseño más avanzados para usar funciones de tabla que se basan en el patrón anterior, donde puede maximizar aún más el procesamiento del conjunto de resultados, pero esta publicación ya es mucho para que la mayoría absorba.