{"id":26263690,"url":"https://github.com/iliangithub/apuntes_pl-sql","last_synced_at":"2026-01-02T04:41:04.406Z","repository":{"id":278287481,"uuid":"934450770","full_name":"iliangithub/Apuntes_PL-SQL","owner":"iliangithub","description":"Apuntes que tengo del Grado Superior, y que he añadido recientemente.","archived":false,"fork":false,"pushed_at":"2025-02-19T00:15:30.000Z","size":143,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-19T01:24:11.422Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iliangithub.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-17T21:21:17.000Z","updated_at":"2025-02-19T00:15:34.000Z","dependencies_parsed_at":"2025-02-19T01:24:14.958Z","dependency_job_id":"14e929e5-a3a9-44bb-b8b5-698d01c31c91","html_url":"https://github.com/iliangithub/Apuntes_PL-SQL","commit_stats":null,"previous_names":["iliangithub/apuntes_pl-sql"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iliangithub%2FApuntes_PL-SQL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iliangithub%2FApuntes_PL-SQL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iliangithub%2FApuntes_PL-SQL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iliangithub%2FApuntes_PL-SQL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iliangithub","download_url":"https://codeload.github.com/iliangithub/Apuntes_PL-SQL/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243505898,"owners_count":20301619,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-14T01:19:14.513Z","updated_at":"2026-01-02T04:41:04.393Z","avatar_url":"https://github.com/iliangithub.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# 1.0 Apuntes PL/SQL\nApuntes que tengo del Grado Superior, y que he añadido recientemente.\n## 1.1.0 Bloques. ¿Qué son?\n\nUn bloque, es donde se escribe el código PL/SQL.\n\n**NO PUEDES** hacer un `SELECT * FROM tabla` y luego poner un bloque (Poner el `BEGIN` y el `END`), el bloque sirve solo si quiero manejar el \"output\" del SELECT usando PL/SQL, es decir, que el bloque sólo sirve para cuando se vaya a usar el PL/SQL.\n\n**No puedo simplemente poner una sentencia SQL sin más.**\n\u003e [!IMPORTANT]\n\u003eTodas las instrucciones que pertenecen al bloque deben estar dentro de la estructura definida: la sección declarativa (opcional), la sección ejecutable (entre `BEGIN` y `END`) y la sección de excepciones (opcional). Esto es porque el compilador PL/SQL espera encontrar esa estructura completa para interpretar y compilar el código.\n\u003e\n\u003eSi colocas una sentencia SQL (como `SELECT * FROM tabla`) fuera de un bloque PL/SQL, Oracle no sabe si esa instrucción forma parte de un bloque procedural o si es una sentencia SQL independiente. Es decir, **no puedes mezclar ambas en un solo contexto sin indicarle al sistema dónde termina un bloque y dónde empieza otro.**\n\u003e\n\n\u003e[!WARNING]\n\u003e🚫Esto es incorrecto:\n\u003e```\n\u003eSELECT * FROM tabla\n\u003e\n\u003eBEGIN\n\u003e   DBMS_OUTPUT.PUT_LINE('Hola Mundo');\n\u003eEND;\n\u003e```\n\u003e\n\u003eNo es válido porque:\n\u003e- El SELECT * FROM tabla se está ejecutando como una sentencia SQL independiente, pero luego el compilador PL/SQL encuentra un bloque y se confunde por la mezcla.\n\u003e- Si lo que buscas es ejecutar ambos, deberías separarlos en diferentes ejecuciones o colocar el SELECT dentro de un bloque PL/SQL (usando, por ejemplo, un cursor o SELECT ... INTO ...).\n\u003e**En resumen, cada tipo de sentencia debe estar en el contexto correcto.**\n\u003e\n\u003e✅Sin embargo, esto si\n\u003e\n\u003e```\n\u003e BEGIN\n\u003e  INSERT INTO empleados (id, nombre) VALUES (1, 'Ana');\n\u003e END;\n\u003e```\n\n## 1.1.1 El bloque en sí.\n\nEs básicamente esto:\n\n```\nBEGIN\n\nEND;\n```\n\nA veces puedes declarar también variables.\n\n```\nDECLARE\n\nBEGIN\n\nEND;\n```\n\nO poder controlar o manejar errores.\n\n```\nDECLARE\n\nBEGIN\n\nEXCEPTION\n\nEND;\n```\n\nVamos a hacer entonces un bloque con un **Hello World**:\n\n```\nBEGIN\n\ndbms_output.put_line('Hola mundo');\n\nEND;\n```\n\n\u003e[!IMPORTANT]\n\u003e En PL/SQL (y en SQL en general), las cadenas de texto se definen utilizando comillas simples. Esto es parte de la sintaxis del lenguaje.\n\u003e\n\u003e- Comillas simples: Se usan para delimitar un literal de cadena. Por ejemplo, 'Hola mundo' indica que se trata de una secuencia de caracteres que se interpretará literalmente.\n\u003e  \n\u003e- Comillas dobles: Se utilizan en otros contextos, por ejemplo, para delimitar identificadores (como nombres de columnas o tablas) que son sensibles a mayúsculas/minúsculas o que contienen espacios.\n\u003e\n\n### 1.1.0.1 Ejecución de programas:\n\n![image](https://github.com/user-attachments/assets/a8ce28db-74cf-4692-8d39-0aa6d96519e7)\n\nSi le doy arriba a \"*Sentencia de Ejecución*\". Me muestra eso, pero no el output que quiero, ¿Porqué?.\n\n\u003e[!WARNING]\n\u003eSi no habilitas DBMS_OUTPUT, el mensaje se envía a un buffer interno y no se muestra en la pantalla, por eso podrías ver solo el mensaje \"Procedimiento PL/SQL terminado correctamente\" sin el contenido que esperas.\n\u003e\n\u003eDebería de quedar algo así:\n\u003e```\n\u003eSET SERVEROUTPUT ON;\n\u003eBEGIN\n\u003e  DBMS_OUTPUT.PUT_LINE('Hola Mundo');\n\u003eEND;\n\u003e/\n\u003e```\n\u003e\n\u003eY para apagarlo pues con el OFF... \n\u003e\n\n\u003e[!TIP]\n\u003e\n\u003e ![image](https://github.com/user-attachments/assets/72963d8f-48bc-4d0b-b6e6-fd74082045ca)\n\u003e\n\u003ePodemos hacerlo también así:\n\u003e\n\u003e ![image](https://github.com/user-attachments/assets/306d81d1-058b-4992-ab54-c019c60e9aae)\n\u003e\n\u003e Y así queda mucho más aislado.\n\u003e\n\u003e ![image](https://github.com/user-attachments/assets/61a883fb-6039-41ac-9bd0-954f9468730e)\n\n### 1.1.0.2 Ejecución de programas SUPER IMPORTANTE POR FAVOR LEER:\n\n\u003e[!IMPORTANT]\n\u003eEl Shortcut para ejecutar los scripts es **`F5`**.\n\u003e\n\u003eHay en realidad, varias maneras, la que he estado haciendo hasta el momento y ejecutar todo el script:\n\u003e\n\u003e ![image](https://github.com/user-attachments/assets/24b6742d-7f97-4550-aab4-97067372aefe)\n\u003e\n\u003eEntonces, la primera opción ejecutaría solo 1 bloque o mejor dicho:\n\u003eSi no seleccionas todo el script antes de ejecutar (usando F5 o el botón de \"Run Script\"), SQL Developer ejecuta solo el bloque en el que se encuentre el cursor. Por ejemplo, si tu cursor está en el segundo bloque, se ejecutará únicamente ese bloque y verás solo el resultado de ese bloque.\n\u003e\n\n## 1.1.2 Cómo tener varios bloques a la vez:\n\nTienen que estar separados por `/`\n\n```\nBEGIN\n DBMS_OUTPUT.PUT_LINE('Hola Mundo');\nEND;\n\n/\n\nBEGIN\n DBMS_OUTPUT.PUT_LINE('Hola Mundo2');\nEND;\n```\n\n**Y como hemos dicho anteriormente, ahora para mostrar los dos Holamundos, tenemos que darle a F5**.\n\n## 1.1.3 Variables.\n\nSe declaran arriba del todo:\n\n```\nDECLARE\n   v_num NUMBER(2) := 10;\n   v_cadena VARCHAR(10) := 'Fernando';\n   v_fecha DATE := SYSDATE;\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('El valor de la primera variable es ' || v_num  );\n   DBMS_OUTPUT.PUT_LINE('El valor de la segunda variable es ' || v_cadena );\n   DBMS_OUTPUT.PUT_LINE('El valor de la tercera variable es ' || v_fecha );\nEND;\n```\n*Y si, literalmente hay que poner `:=` como si fuera un xxxxxx.*\n\nEntonces...\n\nAcabamos de ver que para declarar variables se hace como se muestra arriba y para concatenar, se hace usando el **`||`**.\n\n\nQue por cierto, también lo podríamos haber hecho así:\n\n```\nDECLARE\n   v_num NUMBER(2) := 10;\n   v_cadena VARCHAR(10) := 'Fernando';\n   v_fecha DATE := SYSDATE;\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('El valor de la primera variable es ' || v_num || ' El valor de la segunda variable es ' || v_cadena );\nEND;\n```\n\nY también el valor de las variables, pues puede variar perfectamente sin problemas...\n\n```\nDECLARE\n   v_num NUMBER(2) := 10;\n\nBEGIN\n   v_num NUMBER(2) := 15;\n   DBMS_OUTPUT.PUT_LINE('El valor de la primera variable es ' || v_num );\nEND;\n```\n\n## 1.1.4 Variables 2, Pedir Datos al Usuario IMPORTANTE.\n\nPara entender esto, es básicamente como un IMPUT que literalmente pide que el Usuario rellene para pues mostrarlo o lo que sea...\n\n```\nDECLARE\n   v_opcion1 NUMBER(2) := \u0026numero_que_va_a_poner_el_usuario1;\n   v_opcion2 NUMBER(2) := \u0026numero_que_va_a_poner_el_usuario2;\n   v_suma_de_numeros NUMBER (3);\n\nBEGIN\n   v_suma_de_numeros := v_opcion1 + v_opcion2;\n   DBMS_OUTPUT.PUT_LINE(v_opcion1 || ' + ' || v_opcion2 || ' es igual a: ' || v_suma_de_numeros);\nEND;\n```\n\nY como podemos ver, nos aparece la ventanita:\n\n![image](https://github.com/user-attachments/assets/9b3196c3-6bf1-42e1-9f27-5c1e2ea3a340)\n\nSi vamos a poner texto en esa ventanita, tenemos que hacerlo con las '' comillas simples.\n\n## 1.1.5 Tipos de datos en PL/SQL.\n- `CHAR (n)` Cadena de longitud fija. *Tienes que saber de antemano la longitud*\n- `VARCHAR2(n)` Cadena de longitud variable.\n- `NUMBER` Sin especificar nada, permite almacenar números con hasta 38 dígitos y la escala es variable.\n- `NUMBER(p, s)` Números con precisión.\n\n\u003e[!IMPORTANT]\n\u003e `p` (Precisión). Es el número total de dígitos que puede tener el número, tanto a la izquierda como a la derecha del punto decimal.\n\u003e\n\u003e `s` (Escala). Es el número de dígitos que se reservan para la parte decimal (a la derecha del punto).\n\u003e\n\u003e`NUMBER(5,2)` permite almacenar números con hasta 5 dígitos en total, de los cuales 2 son decimales. Esto significa que puedes tener números como `123.45` o `-99.99`.\n\u003e\n\u003e- Si intentas almacenar `1234.56`, excederías la precisión total de 5 dígitos.\n\u003e- Si intentas almacenar `12.345`, excederías la escala permitida (más de 2 dígitos decimales).\nEsta notación ayuda a controlar la cantidad y la precisión de los datos numéricos que se almacenan en la base de datos.\n\u003e\n\u003e**Cuando se intenta almacenar un valor que excede la precisión o la escala definida**, Oracle no realiza un ajuste o truncamiento automático, sino que **lanza un error en tiempo de ejecución**. Algunos ejemplos de errores que podrías ver son:\n\u003e\n\u003e- ORA-06502: PL/SQL: numeric or value error: Indica que el valor numérico no se ajusta al formato esperado.\n\u003e- ORA-12899: value too large for column (si es en una columna de tabla): Indica que el valor es mayor que el tamaño permitido.\n\n\n- `DATE` Para almacenar fechas y horas (hasta segundos).\n- `TIMESTAMP` Fecha y hora con mayor precisión, incluyendo fracciones de segundo (milisegundos o incluso microsegundos). Ideal para aplicaciones donde se requiere una precisión más fina en el registro de eventos, como en logs, transacciones o cuando se necesita medir intervalos muy cortos.\n- `BOOLEAN` ( Que pueden ser `TRUE`, `FALSE` y `NULL`. )\n- `CLOB/BLOB`, para datos grandes como textos muy extensos o imágenes.\n- `Tipos compuestos y colecciones` que trataremos más adelante.\n\n## 1.2.0 Operadores Aritméticos:\n\nAntes hemos hecho la suma, vamos a probarlas todas de nuevo:\n\n### 1.2.0.1 Suma.\n\n```\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('Suma (2 + 3): ' || (2 + 3));\nEND;\n/\n```\n\n### 1.2.0.2 Resta.\n\n```\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('Resta (5 - 3): ' || (5 - 3));\nEND;\n/\n```\n\n### 1.2.0.3 Multiplicación.\n\n```\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('Multiplicación (4 * 2): ' || (4 * 2));\nEND;\n/\n```\n\n### 1.2.0.4 División.\n\n```\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('División (10 / 2): ' || (10 / 2));\nEND;\n/\n```\n\nNo se puede dividir entre 0.\n\n### 1.2.0.5 Exponenciación.\n\n```\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('Exponenciación (2 ** 3): ' || (2 ** 3));\nEND;\n/\n```\n\n### 1.2.0.6 El resto.\n\n```\nBEGIN\n   DBMS_OUTPUT.PUT_LINE('Módulo (MOD(10, 3)): ' || MOD(10, 3));\nEND;\n/\n```\n## 1.2.1 Operadores Lógicos y Relacionales o de Comparación:\n\nLos operadores relacionales o de comparación son:\n- `\u003e`\n- `\u003e=`\n- `\u003c=`\n- `\u003c`\n- `=` (este es de comparación, no es de asignación, de asignarle un valor, como era el `:=`).\n- `!=` (y este es el distinto, el que no es X cosa. también se puede usar `\u003c\u003e`).\n  \n### 1.2.1.1 Operador Lóg. AND.\n\n```\nDECLARE\n   v_numero1 NUMBER(2) := 1;\n   v_numero2 NUMBER(2) := 2;\n   v_numero3 NUMBER(2) := 3;\n\nBEGIN\n   IF v_numero1 \u003e= v_numero2 AND v_numero1 \u003e= v_numero3 THEN\n        DBMS_OUTPUT.PUT_LINE(v_numero1 || ' es el mayor número de entre esos.');\n   END IF;\nEND;\n```\nEn este caso, no va a devolver nada.\n\n### 1.2.1.2 Operador Lóg. OR.\n\nLo mismo que para antes\n\n### 1.2.1.3 Operador Lóg. NOT.\n\nLo mismo que para lo de antes.\n\n## 1.3.0 Estructuras de Control:\n\n### 1.3.1 IF:\n\nVamos a hacer un ejercicio muy sencillo:\n\nEsta es la sintáxis.\n```\nBEGIN\n\n   IF    la condición   THEN\n                         ;\n   END IF;\n\nEND;\n```\n\ny este es el ejercicio, va a comparar el valor numérico de las 3 variables:\n```\nDECLARE\n   v_numero1 NUMBER(2) := 1;\n   v_numero2 NUMBER(2) := 2;\n   v_numero3 NUMBER(2) := 3;\n\nBEGIN\n   IF v_numero1 \u003e= v_numero2 AND v_numero1 \u003e= v_numero3 THEN\n        DBMS_OUTPUT.PUT_LINE(v_numero1 || ' es el mayor número de entre esos.');\n   END IF;\nEND;\n```\n\nY **NO DEBERÍA de mostrar nada en pantalla, porque el 1 es menor que esos dos.**\n\n```\nDECLARE\n   v_numero1 NUMBER(2) := 1;\n   v_numero2 NUMBER(2) := 2;\n   v_numero3 NUMBER(2) := 3;\n\nBEGIN\n   IF v_numero1 \u003e= v_numero2 AND v_numero1 \u003e= v_numero3 THEN\n        DBMS_OUTPUT.PUT_LINE(v_numero1 || ' es el mayor número de entre esos.');\n   ELSIF v_numero2 \u003e= v_numero3 THEN\n        DBMS_OUTPUT.PUT_LINE(v_numero2 || ' es el mayor número de entre esos.'); \n   ELSE\n        DBMS_OUTPUT.PUT_LINE(v_numero3 || ' es el mayor número de entre esos.'); \n   END IF;\n\nEND;\n```\n\n**Aquí como vemos, manejamos `elsif`\n\n### 1.3.2 Case:\n\nEs casi que lo mismo que el if.\n\n```\nDECLARE\n    v_dia_introducido NUMBER(2) := \u0026dia;\nBEGIN\n\n    CASE v_dia_introducido\n        WHEN 1 THEN\n           DBMS_OUTPUT.PUT_LINE('Lunes');\n        WHEN 2 THEN\n           DBMS_OUTPUT.PUT_LINE('Martes');\n        WHEN 3 THEN\n           DBMS_OUTPUT.PUT_LINE('Miércoles');\n        WHEN 4 THEN\n           DBMS_OUTPUT.PUT_LINE('Jueves');\n        ELSE\n          DBMS_OUTPUT.PUT_LINE('Descansitoo');\n      END CASE;\nEND;\n```\n\n### 1.3.3 Bucle For:\nRecordemos que esto sirve para cuando sabemos con exactitud cuando termina.\n\n```\nBEGIN\n    DBMS_OUTPUT.PUT_LINE('Esto es un bucle for');\n    FOR (v_num_inicial in 1..10) LOOP\n        DBMS_OUTPUT.PUT_LINE(v_num_inicial);\n    END LOOP;\nEND;\n```\n\n¿Y como hace para ir de 2 en 2?\n\n```\nBEGIN\n    DBMS_OUTPUT.PUT_LINE('Esto es un bucle for');\n    FOR v_num_inicial IN 1..10 BY 2 LOOP\n        DBMS_OUTPUT.PUT_LINE(v_num_inicial);\n    END LOOP;\nEND;\n```\n\nY también podemos hacerlo al revés, de 10 a 1:\n\n```\nBEGIN\n    DBMS_OUTPUT.PUT_LINE('Esto es un bucle for');\n    FOR v_num_inicial IN REVERSE 1..10 LOOP\n        DBMS_OUTPUT.PUT_LINE(v_num_inicial);\n    END LOOP;\nEND;\n```\n\n### 1.3.4 Bucle WHILE:\n\n```\nDECLARE\n    v_num_inicial NUMBER(2) := 1;\nBEGIN\n    DBMS_OUTPUT.PUT_LINE('Esto es un bucle while');\n    WHILE (v_num_inicial \u003c= 10) LOOP\n        DBMS_OUTPUT.PUT_LINE(v_num_inicial);\n        v_num_inicial := v_num_inicial + 1;\n    END LOOP;\nEND;\n```\n### 1.3.5 Bucle LOOP:\n\nEl `EXIT WHEN` podemos ponerlo donde queramos, es útil para los cursores.\n\n```\nDECLARE\n    v_num_inicial NUMBER(2) := 1;\nBEGIN\n    DBMS_OUTPUT.PUT_LINE('Esto es un bucle LOOP');\n\n    LOOP\n       \n        DBMS_OUTPUT.PUT_LINE(v_num_inicial);\n        EXIT WHEN v_num_inicial = 10;\n        v_num_inicial := v_num_inicial + 1;\n    END LOOP;\nEND;\n```\n## 1.4 Arrays:\n\nPara declarar un array, primero tenemos que declarar un tipo: **`TYPE alumnos_arr IS VARRAY(3) OF VARCHAR2 (20);`**\n```\nDECLARE\n   TYPE alumnos_arr IS VARRAY(3) OF VARCHAR2(20);\n   v_alumnos alumnos_arr := alumnos_arr('Fernando', 'Manuel', 'Roberto');\n\n   TYPE notas_arr IS VARRAY(3) OF NUMBER(2);\n   v_notas notas_arr := notas_arr('10','5','6');\n\nBEGIN\n   FOR v_num_inicial IN 1..v_alumnos.count LOOP\n       DBMS_OUTPUT.PUT_LINE('El alumno: ' || v_alumnos(v_num_inicial) || ' y su nota es de: ' || v_notas(v_num_inicial));\n   END LOOP;\nEND;\n```\n## 1.5 SELECT INTO:\n\n```\nDECLARE\n   v_total NUMBER(8);\nBEGIN\n   SELECT count(*) INTO v_total FROM producto;\n\n   DBMS_OUTPUT.PUT_LINE('Total de productos: ' || v_total);\nEND;\n```\n\nSi quisieramos almacenar varias cosas en varias variables:\n\n```\nDECLARE\n   v_total NUMBER(8);\n   v_suma_productos NUMBER(8);\nBEGIN\n   SELECT count(*),sum(precio) INTO v_total, v_suma_productos FROM producto;\n\n   DBMS_OUTPUT.PUT_LINE('Total de productos: ' || v_total);\n   DBMS_OUTPUT.PUT_LINE('Y el precio total es de: ' || v_suma_productos);\nEND;\n```\n\u003e[!IMPORTANT]\n\u003eSELECT INTO, **Siempre tiene que devolver 1 valor/registro**.\n\u003e- Más de una fila: Se genera la excepción ORA-01422: exact fetch returns more than requested number of rows.\n\u003e- Ninguna fila: Se lanza la excepción NO_DATA_FOUND.\n\u003e\n\n## 1.6 Atributo %TYPE y %ROWTYPE:\n\n### 1.6.1 %TYPE:\n\nEl atributo %TYPE se utiliza en PL/SQL para declarar una variable con el mismo tipo de datos que otra columna de una tabla o que otra variable. Esto tiene varias ventajas:\n\n- **Consistencia:** Si cambias el tipo de dato de la columna en la tabla, las variables declaradas con %TYPE se actualizan automáticamente.\n- **Facilidad de mantenimiento:** Evitas repetir el tipo de dato en el código, reduciendo errores y manteniendo la coherencia.\n\n\u003e[!IMPORTANT]\n\u003eNo, no se utiliza %TYPE porque no sepas cuál es el tipo de dato, sino para garantizar consistencia y facilitar el mantenimiento.\n\u003e\n\n```\nDECLARE\n    v_codigo producto.codigo%TYPE :=  \u0026codigo;\n    v_nombre producto.nombre%TYPE;\nBEGIN\n    SELECT nombre INTO v_nombre FROM producto WHERE codigo = v_codigo;\n    DBMS_OUTPUT.PUT_LINE('Nombre del producto ' || v_nombre || 'y el código es: ' || v_codigo);\nEND;\n```\n\nLa tabla es \"producto\" y \"código\", una simple columna de muchas.\n\n### 1.6.2 %ROWTYPE:\n\nSi quiero almacenar toda una fila.\n\n```\nDECLARE\n    v_codigo producto.codigo%TYPE :=  \u0026codigo;\n    v_producto producto%ROWTYPE;\n\nBEGIN\n\n    SELECT * INTO v_producto FROM producto WHERE codigo = v_codigo;\n\n    -- Entonces, toda la fila está almacenada en v_producto, voy a ir mostrandola de poco a poco.\n    DBMS_OUTPUT.PUT_LINE('Nombre del producto: ' || v_producto.nombre );\n    DBMS_OUTPUT.PUT_LINE('Código del producto: ' || v_codigo );\n    DBMS_OUTPUT.PUT_LINE('Fabricante del producto: ' || v_producto.fabricante );\n    DBMS_OUTPUT.PUT_LINE('Precio del producto: ' || v_producto.precio );\n\nEND;\n```\n\n## 1.7 Excepciones o manejos de errores:\n\nhttps://www.ibm.com/docs/es/db2/11.5?topic=plsql-exception-handling\n\nSe hace con el EXCEPTION\n\n```\nDECLARE\n    v_codigo producto.codigo%TYPE :=  \u0026codigo;\n    v_producto producto%ROWTYPE;\n\nBEGIN\n\n    SELECT * INTO v_producto FROM producto WHERE codigo = v_codigo;\n\n    DBMS_OUTPUT.PUT_LINE('Nombre del producto: ' || v_producto.nombre );\n    DBMS_OUTPUT.PUT_LINE('Código del producto: ' || v_codigo );\n    DBMS_OUTPUT.PUT_LINE('Fabricante del producto: ' || v_producto.fabricante );\n    DBMS_OUTPUT.PUT_LINE('Precio del producto: ' || v_producto.precio );\n\nEXCEPTION\n    WHEN no_data_found THEN\n         DBMS_OUTPUT.PUT_LINE('No existe el producto '|| v_codigo);\n    WHEN others THEN\n         DBMS_OUTPUT.PUT_LINE('Error');\nEND;\n```\n\n### 1.7.2 Excepciones propias y Raise:\n\nAntes vimos las excepciones, aunque también nosotros podemos crearlas.\n\n```\nDECLARE\n    v_codigo producto.codigo%TYPE :=  \u0026codigo;\n    v_producto producto%ROWTYPE;\n\n    limite_precio EXCEPTION;\nBEGIN\n\n    SELECT * INTO v_producto FROM producto WHERE codigo = v_codigo;\n\n    IF v_producto.precio \u003e= THEN\n\n       RAISE limite_precio;\n       --Para lanzar la excepción se hace con un Raise.\n    END IF;\n\n    DBMS_OUTPUT.PUT_LINE('Nombre del producto: ' || v_producto.nombre );\n    DBMS_OUTPUT.PUT_LINE('Código del producto: ' || v_codigo );\n    DBMS_OUTPUT.PUT_LINE('Fabricante del producto: ' || v_producto.fabricante );\n    DBMS_OUTPUT.PUT_LINE('Precio del producto: ' || v_producto.precio );\n \nEXCEPTION\n    WHEN no_data_found THEN\n         DBMS_OUTPUT.PUT_LINE('No existe el producto '|| v_codigo);\n    WHEN limite_precio THEN\n         DBMS_OUTPUT.PUT_LINE('Se ha superado el limite '|| v_codigo);\n    WHEN others THEN\n         DBMS_OUTPUT.PUT_LINE('Error');\nEND;\n```\n\n### 1.7.2 RAISE_APPLICATION_ERROR:\n\nEs parecido al anterior, pero este es a nivel de aplicación.\n\n## 1.8 Procedimientos:\n\nSi queremos reutilizar parte de nuestro código, lo hacemos usando los procedimientos o también podemos utilizar las funciones.\n\nLa diferencia principal entre el procedimiento y la funcion, es que el procedimiento NO devuelve nada.\n\n**Función:**\n- Debe devolver un valor mediante la cláusula RETURN.\n- Pueden ser invocadas en una sentencia SQL si son determinísticas y cumplen ciertos requisitos.\n- Se usa cuando se necesita calcular y retornar un valor a partir de unos parámetros.\n\n**Procedimiento:**\n- No está obligado a devolver un valor.\n- Puede devolver valores mediante parámetros OUT o IN OUT, pero no a través de RETURN.\n- No se pueden invocar directamente dentro de sentencias SQL, se ejecutan mediante bloques PL/SQL o llamadas desde aplicaciones.\n- Se utiliza para realizar una acción o proceso, como insertar registros, actualizar datos, etc.\n\n\nEn resumen, la principal diferencia es que una **función devuelve un valor y puede utilizarse en expresiones SQL**, mientras que un procedimiento se utiliza para ejecutar acciones sin requerir devolver un valor directamente.\n\n\u003e[!IMPORTANT]\n\u003eLas funciones están diseñadas para ser \"puras\", es decir, para calcular y retornar un valor sin causar efectos secundarios (como modificar datos en la base de datos). Esto es especialmente importante cuando se usan en sentencias SQL, ya que Oracle espera que las funciones llamadas en SQL no alteren el estado de la base de datos. Si una función modifica datos, pueden ocurrir errores o comportamientos inesperados.\n\u003e\n\u003ePorque claro, la pregunta que me surge es si puedo INSERT en una función y no.\n\u003e\n\u003eLas operaciones en la función tienen que ser determinísticas y sin efectos secundarios.\n\n```\nCREATE OR REPLACE PROCEDURE infoProducto()\n```\n\n## 1.9 Funciones:\n\n## 1.10 Cursores:\n\nLos cursores nos permiten almacenar y recorrer un conjunto de datos.\n\nUn cursor en PL/SQL es un mecanismo que permite manejar el resultado de una consulta SELECT de manera fila por fila. Se usan cuando una consulta devuelve más de una fila y se necesita procesarlas individualmente.\n\nEstos pueden ser implícitos o explícitos:\n\n- Los implícitos son creados automáticamente por Oracle cuando se ejecuta una consulta que devuelve una sola fila.\n- Y el explícito es cuando una consulta devuelve múltiples filas, debemos usar un cursor explícito para recorrerlas una por una.\n\n# 1.11 Triggers:\n\nSon códigos SQL que se ejecutan cuando modificamos la BD.\n\n```\nCREATE OR REPLACE TRIGGER trg_auditar_salario\nBEFORE UPDATE ON empleados\nFOR EACH ROW\nWHEN (NEW.salario \u003c\u003e OLD.salario) -- Solo si el salario cambia\n\nBEGIN\n    INSERT INTO historial_salarios (id_empleado, salario_anterior, salario_nuevo)\n    VALUES (:OLD.id, :OLD.salario, :NEW.salario);\nEND;\n/\n```\n# 2.0 Preguntas Entrevista:\n\n- **Conceptos básicos de PL/SQL**\n  - ¿Qué es PL/SQL y en qué se diferencia de SQL?\n  - Explica la estructura básica de un bloque PL/SQL.\n  - ¿Qué son las excepciones en PL/SQL? ¿Cuáles son las más comunes?\n  \n- **Procedimientos, Funciones y Paquetes**\n  - ¿Cuál es la diferencia entre un procedimiento y una función en PL/SQL?\n  - ¿Qué ventajas tiene usar paquetes en PL/SQL?\n  - ¿Cómo se declara y ejecuta un procedimiento almacenado?\n- **Cursores y Manejo de Datos**\n  - ¿Qué es un cursor en PL/SQL? ¿Cuál es la diferencia entre un cursor implícito y un cursor explícito?\n  - ¿Cómo se usa un cursor FOR LOOP?\n  - ¿Cuáles son las diferencias entre %ROWTYPE y %TYPE?\n\n- **Triggers y Excepciones**\n  - ¿Qué es un trigger y para qué se usa?\n  - ¿Cuáles son los tipos de triggers en PL/SQL?\n  - ¿Cómo manejas excepciones en PL/SQL?\n\n- **Optimización y Buenas Prácticas**\n  - ¿Cómo mejorar el rendimiento de un bloque PL/SQL?\n  - ¿Qué es la colección BULK y cuándo se usa?\n  - ¿Cómo optimizar el uso de cursores para mejorar la performance?\n\n- **DML y Transacciones**\n  - ¿Qué es un SAVEPOINT y cómo se usa en PL/SQL?\n  - ¿Cómo funciona el COMMIT y el ROLLBACK?\n  - ¿Qué diferencia hay entre DELETE y TRUNCATE?\n\n- **Consultas Prácticas**\n  - \"Escribe un procedimiento almacenado que reciba un ID de empleado y devuelva su salario.\"\n\n## PREGUNTA 1: ¿Qué es PL/SQL y en qué se diferencia de SQL?\n\nNo es que se diferencie, es un lenguaje procedimental. Un lenguaje adicional a SQL. SQL tiene varios sublenguajes, entre ellos:\n- DML\n- DDL\n- DCL\n- TCL\n\ny entre ellos **NO ESTÁ el PL/SQL.**\n\n- PL/SQL\n\nPL/SQL es entonces, un lenguaje de programación desarrollado por Oracle, que amplica las posibilidades y funcionalidades de SQL, al poder manipular memoria y usar estrucutras de control, como los For, While, If.\n\n\u003e[!TIP]\n\u003eUn lenguaje procedimental es un tipo de lenguaje de programación basado en la ejecución secuencial de instrucciones organizadas en procedimientos o funciones. Estos lenguajes siguen un enfoque estructurado donde el código se divide en bloques reutilizables que realizan tareas específicas.\n\u003e\n\n## PREGUNTA 2: Explica la estructura básica de un bloque PL/SQL.\n\n```\nDECLARE  -- (Opcional)\n   -- Declaración de variables, constantes, cursores, etc.\nBEGIN  -- (Obligatorio)\n   -- Código ejecutable (consultas, asignaciones, lógica de negocio, etc.)\nEXCEPTION  -- (Opcional)\n   -- Manejo de errores (cuando ocurren excepciones)\nEND;\n/\n```\n\n- DECLARE, sirve para declarar las variables, los %ROWTYPE y %TYPE y también excepciones que se usarán en ese bloque.\n- BEGIN, es donde contendrá el código en sí.\n- EXCEPTION, control y manejo de errores.\n- END, para marcar el final del bloque\n- / se usa si quieres poner debajo otro bloque.\n\n## PREGUNTA 3: ¿Qué son las excepciones en PL/SQL? ¿Cuáles son las más comunes?\n\nSon eventos que interrumpen el flujo normal de ejecución de un bloque de código debido a un error en tiempo de ejecución. Estas excepciones pueden ser manejadas con la sección EXCEPTION para evitar que el programa falle inesperadamente.\n\nHay de dos tipos:\n\n1️⃣**Las predefinidas, que para eso habrá que mirar la documentación.**\n\n\n| Excepción              | Error Code  | Descripción |\n|------------------------|------------|--------------------------------------------------|\n| `NO_DATA_FOUND`       | ORA-01403   | No se encontraron filas en una consulta `SELECT INTO`. |\n| `TOO_MANY_ROWS`       | ORA-01422   | Una consulta `SELECT INTO` devolvió más de una fila. |\n| `ZERO_DIVIDE`         | ORA-01476   | Se intentó dividir un número por cero. |\n| `INVALID_NUMBER`      | ORA-01722   | Conversión inválida de caracteres a número. |\n| `VALUE_ERROR`        | ORA-06502   | Error de desbordamiento numérico o cadena demasiado larga. |\n| `CURSOR_ALREADY_OPEN` | ORA-06511   | Se intentó abrir un cursor que ya estaba abierto. |\n\n2️⃣**Definidas por el programador (personalizadas)**\n\nTú puedes definir tus propias excepciones, como:\n\n```\nmy_error EXCEPTION;\n```\n\nY luego lanzarla con:\n\n```\nRAISE my_error;\n```\n\n## PREGUNTA 4: ¿Cuál es la diferencia entre un procedimiento y una función en PL/SQL?\n\nLa diferencia principal, es que:\n\n- Las funciones, necesitan tener comandos determinísticos y que no alteren la BD, y devuelve un OUTPUT, un resultado. Se ejecuta en expresiones.\n- El procedimiento no necesariamente sirve para devolver resultados, si no que realiza tareas. (acciones, transacciones, validaciones). Se ejecuta con un `EXEC` dentro del bloque.\n\n## PREGUNTA 10: ¿Qué es un trigger y para qué se usa?\n\nUn trigger (o disparador) es un bloque de código PL/SQL que se asocia a una tabla o vista y que se ejecuta automáticamente en respuesta a eventos específicos en la base de datos, como pueden ser operaciones de INSERT, UPDATE o DELETE.\n\n¿Para qué se usa?\n- **Mantener la integridad de los datos:**\n  Puedes usar triggers para asegurarte de que se cumplan ciertas reglas o restricciones de negocio cada vez que se modifica la tabla. Por ejemplo, evitar que se inserten valores fuera de un rango permitido.\n\n- **Automatizar procesos:**\n  Permiten ejecutar acciones automáticamente cuando ocurre un evento, como actualizar datos en tablas relacionadas, enviar notificaciones o registrar auditorías.\n\n- **Validación y seguridad:**\n  Se pueden utilizar para validar datos antes de que se inserten o actualicen, o incluso para prevenir cambios que puedan violar la integridad referencial.\n\n- **Auditoría de cambios:**\n  Es común utilizar triggers para llevar un registro de quién realizó cambios en la base de datos y cuándo, insertando registros en tablas de auditoría.\n\n## PREGUNTA 21: ¿Qué diferencia hay entre DELETE y TRUNCATE?\n\n**TRUNCATE TABLE:**\nEs una sentencia DDL que elimina todas las filas de la tabla de manera rápida y eficiente, reseteando, en muchos casos, los contadores de auto_increment.\n\n**y el DELETE es DML:**\nSi necesitas eliminar solo ciertos registros de una tabla, debes utilizar la sentencia DELETE junto con la cláusula WHERE.\n\nSi quisieramos eliminar la cuenta entera de un usuario, sus compras, etc... Necesitamos:\n\n**Eliminar manualmente en el orden correcto.**\n\nSi no tienes configurado el ON DELETE CASCADE, deberás borrar los datos de las tablas hijas primero y luego el registro del usuario. Esto es importante para no violar la integridad referencial.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filiangithub%2Fapuntes_pl-sql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filiangithub%2Fapuntes_pl-sql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filiangithub%2Fapuntes_pl-sql/lists"}