Compare commits

..

21 Commits

Author SHA1 Message Date
3b534f5f1f vault backup: 2026-01-12 00:35:30 2026-01-12 00:35:30 +01:00
5c93796a8d vault backup: 2025-12-27 17:48:05 2025-12-27 17:48:05 +01:00
dd282a64ac vault backup: 2025-12-17 20:31:17 2025-12-17 20:31:17 +01:00
314c84cd90 Añadido el apartado Proyectos.
Añadido el proyecto LilCMS.JS en proyectos
Añadido las notas de versión 0.0.1 de LilCMS.JS
2025-12-16 18:48:00 +01:00
75ec755378 vault backup: 2025-12-16 01:18:48 2025-12-16 01:18:48 +01:00
ccbc2f4a29 vault backup: 2025-12-16 00:07:49 2025-12-16 00:07:49 +01:00
9b83a7a6bb Creado Apuntes generales de SQL 2025-12-15 01:07:57 +01:00
58f69d5330 Creado la carpeta de tecnicisomos y Uso general en SQL.
Creado Notas para clase en SQL.
2025-12-14 22:00:26 +01:00
660e4aed75 Disparadores 2025-12-14 17:51:15 +01:00
845ac5c3aa lista de actividades 2025-12-14 02:03:14 +01:00
296a9644c4 Validador de contraseñas completado. 2025-12-14 01:36:11 +01:00
424f765ef6 Cambios en el readme 2025-12-13 22:00:11 +01:00
d5afc2358c Procedimientos almacenados y disparadores. 2025-12-13 21:34:23 +01:00
58150b984f vault backup: 2025-12-12 22:34:40 2025-12-12 22:34:40 +01:00
b6bd7ab8e8 vault backup: 2025-12-12 17:45:02 2025-12-12 17:45:02 +01:00
539b853443 vault backup: 2025-12-12 16:16:32 2025-12-12 16:16:32 +01:00
c4e434d919 vault backup: 2025-12-12 15:58:00 2025-12-12 15:58:00 +01:00
b623e68c5e vault backup: 2025-12-12 15:23:40 2025-12-12 15:23:40 +01:00
0e83a49bc9 Vault backup 2025-12-12 15:23:16 +01:00
ed446a8469 vault backup: 2025-12-12 15:22:45 2025-12-12 15:22:45 +01:00
be701009ba vault backup: 2025-12-11 17:33:13 2025-12-11 17:33:13 +01:00
30 changed files with 1436 additions and 100 deletions

48
.obsidian/graph.json vendored
View File

@@ -5,27 +5,27 @@
"showAttachments": false, "showAttachments": false,
"hideUnresolved": false, "hideUnresolved": false,
"showOrphans": true, "showOrphans": true,
"collapse-color-groups": false, "collapse-color-groups": true,
"colorGroups": [ "colorGroups": [
{ {
"query": "tag:#SQL-ASIX ", "query": "tag:#SQL-ASIX ",
"color": { "color": {
"a": 1, "a": 1,
"rgb": 16514816 "rgb": 16776960
} }
}, },
{ {
"query": "file:\"Mecánica Unix - Manejo de la shell\"", "query": "tag:#M9-PHP ",
"color": { "color": {
"a": 1, "a": 1,
"rgb": 16762711 "rgb": 7292927
} }
}, },
{ {
"query": "tag:#Temario-BSA ", "query": "tag:#Python ",
"color": { "color": {
"a": 1, "a": 1,
"rgb": 8927487 "rgb": 255
} }
}, },
{ {
@@ -36,7 +36,7 @@
} }
}, },
{ {
"query": "file:\"README.md\"", "query": "tag:#Temario-BSA ",
"color": { "color": {
"a": 1, "a": 1,
"rgb": 5431473 "rgb": 5431473
@@ -46,7 +46,35 @@
"query": "tag:#M5-ASIX ", "query": "tag:#M5-ASIX ",
"color": { "color": {
"a": 1, "a": 1,
"rgb": 5419488 "rgb": 16711705
}
},
{
"query": "tag:#LDM-JavaScript ",
"color": {
"a": 1,
"rgb": 16770667
}
},
{
"query": "tag:#LDM-HTML ",
"color": {
"a": 1,
"rgb": 16741120
}
},
{
"query": "tag:#LDM-CSS ",
"color": {
"a": 1,
"rgb": 137142
}
},
{
"query": "file:README.md",
"color": {
"a": 1,
"rgb": 0
} }
} }
], ],
@@ -55,11 +83,11 @@
"textFadeMultiplier": 0, "textFadeMultiplier": 0,
"nodeSizeMultiplier": 1, "nodeSizeMultiplier": 1,
"lineSizeMultiplier": 1, "lineSizeMultiplier": 1,
"collapse-forces": true, "collapse-forces": false,
"centerStrength": 0.518713248970312, "centerStrength": 0.518713248970312,
"repelStrength": 10, "repelStrength": 10,
"linkStrength": 1, "linkStrength": 1,
"linkDistance": 250, "linkDistance": 250,
"scale": 0.2962962962962957, "scale": 0.23309818327823928,
"close": true "close": true
} }

View File

@@ -6,5 +6,14 @@
], ],
"key": "AltGraph" "key": "AltGraph"
} }
],
"editor:insert-codeblock": [
{
"modifiers": [
"Mod",
"Shift"
],
"key": "C"
}
] ]
} }

View File

@@ -23,7 +23,7 @@
"marginBottom": "10", "marginBottom": "10",
"marginLeft": "10", "marginLeft": "10",
"marginRight": "10", "marginRight": "10",
"displayHeader": false, "displayHeader": true,
"displayFooter": true, "displayFooter": true,
"cssSnippet": "0" "cssSnippet": "0"
} }

View File

@@ -12,11 +12,14 @@
"bash", "bash",
"Bash", "Bash",
"Git", "Git",
"subshells" "regex",
"subshells",
"trigger"
], ],
"syncDictionary": false, "syncDictionary": false,
"remoteDictionary": [], "remoteDictionary": [],
"pickyMode": false, "pickyMode": false,
"longCheckNotification": true, "longCheckNotification": true,
"staticLanguage": "es-ES" "staticLanguage": "es-ES",
"disabledRules": "AGREEMENT_POSTPONED_ADJ,WRONG_IMPERATIVE"
} }

View File

@@ -0,0 +1,62 @@
{
"commitMessage": "vault backup: {{date}}",
"autoCommitMessage": "vault backup: {{date}}",
"commitMessageScript": "",
"commitDateFormat": "YYYY-MM-DD HH:mm:ss",
"autoSaveInterval": 0,
"autoPushInterval": 0,
"autoPullInterval": 0,
"autoPullOnBoot": false,
"autoCommitOnlyStaged": false,
"disablePush": false,
"pullBeforePush": true,
"disablePopups": false,
"showErrorNotices": true,
"disablePopupsForNoChanges": false,
"listChangedFilesInMessageBody": false,
"showStatusBar": true,
"updateSubmodules": false,
"syncMethod": "merge",
"customMessageOnAutoBackup": false,
"autoBackupAfterFileChange": false,
"treeStructure": false,
"refreshSourceControl": true,
"basePath": "",
"differentIntervalCommitAndPush": false,
"changedFilesInStatusBar": false,
"showedMobileNotice": true,
"refreshSourceControlTimer": 7000,
"showBranchStatusBar": true,
"setLastSaveToLastCommit": false,
"submoduleRecurseCheckout": false,
"gitDir": "",
"showFileMenu": true,
"authorInHistoryView": "hide",
"dateInHistoryView": false,
"diffStyle": "split",
"lineAuthor": {
"show": false,
"followMovement": "inactive",
"authorDisplay": "initials",
"showCommitHash": false,
"dateTimeFormatOptions": "date",
"dateTimeFormatCustomString": "YYYY-MM-DD HH:mm",
"dateTimeTimezone": "viewer-local",
"coloringMaxAge": "1y",
"colorNew": {
"r": 255,
"g": 150,
"b": 150
},
"colorOld": {
"r": 120,
"g": 160,
"b": 255
},
"textColorCss": "var(--text-muted)",
"ignoreWhitespace": false,
"gutterSpacingFallbackLength": 5,
"lastShownAuthorDisplay": "initials",
"lastShownDateTimeFormatOptions": "date"
}
}

View File

@@ -4,21 +4,21 @@
"type": "split", "type": "split",
"children": [ "children": [
{ {
"id": "b58fb90f35f0b269", "id": "70040d3480c4df8e",
"type": "tabs", "type": "tabs",
"children": [ "children": [
{ {
"id": "5bb933ee810d2703", "id": "43ea8ee768c9bbae",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "markdown", "type": "markdown",
"state": { "state": {
"file": "BDD/SQL/Tipos de datos.md", "file": "Documentación personal/Proyectos/LilCMS/JS_Version/Producción/v0.0.1/LilCMS.JS - v0.0.1 - Notas de versión.md",
"mode": "source", "mode": "source",
"source": false "source": false
}, },
"icon": "lucide-file", "icon": "lucide-file",
"title": "Tipos de datos" "title": "LilCMS.JS - v0.0.1 - Notas de versión"
} }
} }
] ]
@@ -78,7 +78,7 @@
} }
], ],
"direction": "horizontal", "direction": "horizontal",
"width": 332.5 "width": 252.5
}, },
"right": { "right": {
"id": "519d5773673c1040", "id": "519d5773673c1040",
@@ -87,6 +87,7 @@
{ {
"id": "ec141e7a66dc7ec8", "id": "ec141e7a66dc7ec8",
"type": "tabs", "type": "tabs",
"dimension": 54.131355932203384,
"children": [ "children": [
{ {
"id": "5a14f2340b0f216e", "id": "5a14f2340b0f216e",
@@ -94,7 +95,7 @@
"state": { "state": {
"type": "backlink", "type": "backlink",
"state": { "state": {
"file": "Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras.md", "file": "BDD/SQL/Apuntes generales de SQL.md",
"collapseAll": false, "collapseAll": false,
"extraContext": false, "extraContext": false,
"sortOrder": "alphabetical", "sortOrder": "alphabetical",
@@ -104,7 +105,7 @@
"unlinkedCollapsed": true "unlinkedCollapsed": true
}, },
"icon": "links-coming-in", "icon": "links-coming-in",
"title": "Enlaces entrantes de Funciones puras" "title": "Enlaces entrantes de Apuntes generales de SQL"
} }
}, },
{ {
@@ -136,21 +137,6 @@
"title": "Todas las propiedades" "title": "Todas las propiedades"
} }
}, },
{
"id": "b786a3cab1b9744b",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Funciones puras.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Esquema de Funciones puras"
}
},
{ {
"id": "e615f9321ff830be", "id": "e615f9321ff830be",
"type": "leaf", "type": "leaf",
@@ -167,16 +153,16 @@
"state": { "state": {
"type": "outgoing-link", "type": "outgoing-link",
"state": { "state": {
"file": "Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Extra.md", "file": "BDD/SQL/Apuntes generales de SQL.md",
"linksCollapsed": false, "linksCollapsed": false,
"unlinkedCollapsed": true "unlinkedCollapsed": true
}, },
"icon": "links-going-out", "icon": "links-going-out",
"title": "Enlaces salientes de Extra" "title": "Enlaces salientes de Apuntes generales de SQL"
} }
}, },
{ {
"id": "ad0b933ded8362ff", "id": "f680d743f846f5b9",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "graph", "type": "graph",
@@ -186,21 +172,27 @@
} }
}, },
{ {
"id": "1085ec9ee94fa683", "id": "3cca92a09a04b814",
"type": "leaf", "type": "leaf",
"state": { "state": {
"type": "oz-calendar", "type": "outline",
"state": {}, "state": {
"icon": "OZCAL_ICON", "file": "Documentación personal/Proyectos/LilCMS/JS_Version/LilCMS.JS - Concepto.md",
"title": "OZ Calendar" "followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Esquema de LilCMS.JS - Concepto"
} }
} }
], ],
"currentTab": 4 "currentTab": 3
}, },
{ {
"id": "7ac2f57f0d4937ed", "id": "7ac2f57f0d4937ed",
"type": "tabs", "type": "tabs",
"dimension": 45.868644067796616,
"children": [ "children": [
{ {
"id": "f3844ce27366e01c", "id": "f3844ce27366e01c",
@@ -216,7 +208,7 @@
} }
], ],
"direction": "horizontal", "direction": "horizontal",
"width": 317.5 "width": 242.5
}, },
"left-ribbon": { "left-ribbon": {
"hiddenItems": { "hiddenItems": {
@@ -230,44 +222,45 @@
"obsidian-git:Open Git source control": false "obsidian-git:Open Git source control": false
} }
}, },
"active": "e615f9321ff830be", "active": "43ea8ee768c9bbae",
"lastOpenFiles": [ "lastOpenFiles": [
"BDD/SQL/SQL.md", "Documentación personal/Proyectos/LilCMS/JS_Version/Producción/v0.0.1/LilCMS.JS - v0.0.1 - Notas técnicas de versión.md",
"BDD/SQL/Sintaxis (Keywords).md", "Documentación personal/Proyectos/LilCMS/JS_Version/Producción/v0.0.1/LilCMS.JS - v0.0.1 - Notas de versión.md",
"BDD/SQL/Procedimientos almacenados.md", "Documentación personal/Proyectos/LilCMS/JS_Version/Conceptos/LilCMS.JS - Concepto.md",
"BDD/SQL/Lenguaje de Manupulación de Datos (DML).md", "Documentación personal/Proyectos/LilCMS/JS_Version/Producción/v0.0.1",
"BDD/SQL/Lenguaje de definición de datos (DDL).md", "Documentación personal/Proyectos/LilCMS/JS_Version/Producción",
"BDD/SQL/Importación y exportación de base de datos.md", "Documentación personal/Proyectos/LilCMS/JS_Version/Conceptos",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Arrays asociativos.md", "BDD/SQL/Apuntes generales de SQL.md",
"Servidores/M5/Actividades/RA2/M5RA2P1.md", "BDD/SQL/Tecnicismos/Tipos de datos.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Extra.md", "Documentación personal/Proyectos/LilCMS/JS_Version",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Procesos hijos y paralelismo.md", "Documentación personal/Proyectos/LilCMS",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Señales.md", "Documentación personal/Proyectos",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Ejercicios/Ejercicios - Funciones puras.md", "Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Ejercicios/Ejercicios - Funciones puras.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Funciones puras.md", "Imagenes/SQL/Mezcla-cartesiana_(CROSS_JOIN).png",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Expansión de parámetros.md", "Imagenes/SQL",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Patrones de diseño en Bash.md", "Imagenes",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Seguridad en scripts.md", "BDD/SQL/Uso general/Importación y exportación de base de datos.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Subshells.md", "BDD/SQL/SQL.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Trampas (traps).md", "BDD/SQL/Uso general",
"BDD/SQL/Tecnicismos/Disparadores.md",
"BDD/SQL/Tecnicismos",
"BDD/SQL/Tecnicismos/Lenguaje de definición de datos (DDL).md",
"BDD/SQL/Tecnicismos/Lenguaje de Manupulación de Datos (DML).md",
"BDD/SQL/Tecnicismos/Procedimientos almacenados.md",
"BDD/SQL/Tecnicismos/Sintaxis (Keywords).md",
"README.md", "README.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Extra.md",
"Documentación personal/Mecanica de Unix/Mecánica Unix - Manejo de la shell.md", "Documentación personal/Mecanica de Unix/Mecánica Unix - Manejo de la shell.md",
"Documentación personal/Mecanica de Unix/Sin nombre", "Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Funciones puras.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Arrays asociativos.md",
"BDD/SQL/Diario/Clase 4 de diciembre.md", "BDD/SQL/Diario/Clase 4 de diciembre.md",
"BDD/SQL/Tipos de datos.md", "Programación/JavaScript/LDM-JavaScript.md",
"Documentación personal/Mecanica de Unix/2. awk-sed-grep/Parsing avanzado de logs.md", "Programación/HTML y CSS/CSS/LDM - CSS.md",
"Programación/HTML y CSS/HTML/LDM - HTML.md",
"Programación/PHP/M9-PHP.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Expansión de parámetros.md",
"Programación/Python/Python.md",
"Servidores/M5/M5 - ASIX.md", "Servidores/M5/M5 - ASIX.md",
"conflict-files-obsidian-git.md",
"Servidores/M5/Actividades",
"Servidores/M5/Actividades/RA2",
"Servidores/M5/x.md",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Ejercicios/calculadora_modular.sh",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras/Ejercicios",
"Documentación personal/Mecanica de Unix/1. Bash Scripting Avanzado/Funciones puras",
"Documentación personal/Mecanica de Unix/2. awk-sed-grep",
"Sin título.base",
"Documentación personal/Mecanica de Unix/Bash Scripting/Sin nombre",
"Documentación personal/Lenguajes de programación absurdos",
"Documentación personal/Seguridad-Informatica/Vista General 1.canvas", "Documentación personal/Seguridad-Informatica/Vista General 1.canvas",
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/software/specops.svg", "Documentación personal/Seguridad-Informatica/1_Imagenes/tools/software/specops.svg",
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/software/satan.png", "Documentación personal/Seguridad-Informatica/1_Imagenes/tools/software/satan.png",
@@ -277,7 +270,6 @@
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/usbkiller.jpg", "Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/usbkiller.jpg",
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/rubberducky.png", "Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/rubberducky.png",
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/pico_tpm_sniffer.webp", "Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/pico_tpm_sniffer.webp",
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/omgcable.webp", "Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/omgcable.webp"
"Documentación personal/Seguridad-Informatica/1_Imagenes/tools/hardware/Flipper Zero.jpg"
] ]
} }

View File

@@ -0,0 +1,595 @@
---
---
#SQL-ASIX - Structured Query Language
Proveniente de [[SQL]]
- Autor: [Lil_Carpi](https://github.com/Lil-Carpi)
- Fecha de creación: 13-14/12/2025
---
```table-of-contents
```
---
#### Video de referencia:
![Aprende SQL](https://youtu.be/uUdKAYl-F7g)
[Aprende SQL](https://youtu.be/uUdKAYl-F7g)
---
## Notas a tener en cuenta
- TODOS los comandos terminan en `;`.
- Para hacer un UPDATE y DELETE, se recomienda usar la PK(`id`) para evitar errores, pero no es obligatorio.
- Todo lo que sale aquí va orientado para MariaDB por terminal, aunque sirve igual para MySQL Workbench, ya que los comandos son los mismos.
- Los ejemplos de este documento son simples, por lo que se usan VARCHAR y INT. Por favor, usad datos correspondientes indicados en [[Tipos de datos]].
- `user` Es una palabra reservada. NO LA USÉIS NUNCA POR USAR. En este caso, están porque facilita el entendimiento y la simplificación. En caso de tener que crear una tabla de usuarios, usad algo como `allusers` o similar, pero NUNCA `user`.
- La sensibilidad a las mayúsculas y minúsculas en MariaDB depende del `collation`. Normalmente, es **case-insensitive**, PERO NO SIEMPRE.
- En producción, el `SELECT *` se ha de listar columnas especificas para evitar problemas de rendimientro y cambios de esquema.
### Tipos de datos
- **INT (INTEGER)**: Números enteros.
- **Float**: Decimales. Cuidado con los errores de precisión.
- **Varchar**: Strings o letras.
- Es recomendable usar mas tipos de datos.
Más información en [[Tipos de datos]]
![[Tipos de datos]]
---
# Bases de datos
## Creación de base de datos:
```SQL
CREATE DATABASE nombre;
```
---
Ver bases de datos
```SQL
SHOW DATABASES;
```
---
### Seleccionar base de datos
```SQL
USE basededatos;
```
---
### Comentarios
```SQL
-- Cualquier cosa que sea escrita despues de "--" será ignorado.
```
---
### Crear tablas:
```SQL
CREATE TABLE animales (
id INT NOT NULL AUTO_INCREMENT,
tipo VARCHAR(255) DEFAULT NULL,
estado VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (id)
);
```
> Entre paréntesis se colocan los datos que se van a almacenar en la tabla.
> Es recomendable separarlos entre espacios y seleccionar el tipo de dato que se va a usar con esa sección. También, se ha de seleccionar la clave primaria e indicarla entre paréntesis.
---
### Insertar datos en tablas:
```SQL
INSERT INTO animales (tipo, estado) VALUES ('Chanchito', 'Feliz');
```
> De la tabla `animales`, se le insertan a las columnas `tipo` y `estado` los valores `'Chanchito'` y `'Feliz'`, en ese orden.
---
### Modificación de tablas:
```SQL
ALTER TABLE animales MODIFY COLUMN id INT NOT NULL AUTO_INCREMENT;
```
> Caso hipotético en el cual la tabla que hemos creado antes no ha tenido la variable `AUTO_INCREMENT` desde un inicio.
> Nota: Se ha de indicar de nuevo el tipo de valor que se le da a la columna `id`, en este caso, `INT`.
---
#### Renombrar tablas
```SQL
RENAME TABLE user TO users;
```
>Renombra la tabla user a users
---
### Listar elementos de una tabla
```SQL
SELECT * FROM animales;
```
> Lista TODAS las columnas de la tabla animales
```SQL
SELECT * FROM ANIMALES WHERE ID = 1;
```
> Lista todas las columnas de la tabla animales donde el ID sea 1
```SQL
SELECT * FROM animales WHERE estado = 'Feliz';
```
> Lista todas las columnas de la tabla animales donde el estado sea 'Feliz'
> Nota: Los strings se pasan entre comillas simples (`''`).
```SQL
SELECT * FROM animales WHERE estado = 'Feliz' AND tipo = 'Chanchito';
```
>Lista todas las columnas de la tabla animales donde el estado sea 'Feliz' Y tipo sea 'Chanchito'.
> Nota: También se puede usar la variable `OR`.
También podemos hacer selecciones con condiciones más, menos, igual, etc:
```SQL
SELECT * FROM users WHERE edad > 20;
```
> Se puede cambiar el `>` por `<`, `>=`, `<=` y `!=`.
Se pueden filtrar también por strings de datos:
```SQL
SELECT * FROM users WHERE email = 'Antonio@fulano.com';
```
> Lista todas las columnas de la tabla users donde el email sea ' Antonio@fulano.com'
#### `BETWEEN`
También se le pueden buscar ENTRE dos valores:
```SQL
SELECT * FROM users WHERE edad BETWEEN 15 and 30;
```
>Lista todas las columnas de la tabla users donde la edad se encuentre entre 15 y 30.
#### `LIKE`
Podemos buscar por valores que "se parece a", con los `%%`:
```SQL
SELECT * FROM users WHERE email LIKE '%gmail%';
```
> Lista todas las columnas de la tabla users donde el email se parezca a ' `%gmail%`'.
> Nota: En este caso, lo único que busca va a ser el valor '`gmail`'. Todo lo que se encuentre alrededor de ese valor será ignorado. También podemos hacer búsquedas como '`fulano%`', que sirve cuando sabemos el nombre del usuario, pero no sabemos que apellido tiene.
> Las búsquedas con `%` al inicio, no usan índices.
#### `ORDER BY`
Se pueden ordenar las consultas por orden ASCENDENTE (`ASC`) y DESCENDENTE (`DESC`):
```SQL
SELECT * FROM users ORDER BY edad ASC;
-- TAMBIEN
SELECT * FROM users ORDER BY edad DESC;
```
#### `max()` y `min()`
Las funciones `max()` y `min()` nos permiten filtrar por la cantidad máxima y mínima entre valores de INT:
```SQL
SELECT max(edad) AS mayor FROM users;
-- Tambien
SELECT min(edad) AS menor FROM users;
```
>
> Nota: `AS` Se utiliza para dar nombre a la consulta que estamos dando.
#### Listar columnas específicas
Podemos filtrar directamente por columnas que nos interesen ver:
```SQL
SELECT id, name FROM users;
```
> Lista la columna id y name de la tabla users.
#### Listar con aliases (`AS`):
En caso de que tengamos un nombre de una columna que queramos ver con otro nombre sin modificarlo, podemos usar `AS`:
```SQL
SELECT id, name AS nombre FROM users;
```
> Lista la columna id y name como nombre de la tabla users.
> Nota: Las PRIMARY KEYS son inmutables ante esta condición.
---
### Update de registros
```SQL
UPDATE animales SET estado = 'Feliz' WHERE id = 3;
```
> Actualiza la tabla animales y pon el estado `'Feliz'` al registro que tenga un `id` de 3.
> Nota: Para hacer un UPDATE y DELETE, se ha de indicar el `id` de lo que se va a borrar o actualizar, aunque no es obligatorio. Se recomienda usar condiciones especificas (normalmente la PK) para evitar modificar/borrar múltiples filas por error.
---
### Borrar registros
```SQL
DELETE FROM animales WHERE id = 3;
```
> Borra el registro de la tabla animales en donde el `id` sea 3.
> Nota: Para hacer un UPDATE y DELETE, se ha de indicar el `id` de lo que se va a borrar o actualizar, aunque no es obligatorio.Se recomienda usar condiciones especificas (normalmente la PK) para evitar modificar/borrar múltiples filas por error.
---
### `LIMIT`
```SQL
SELECT * FROM users LIMIT 2;
```
> Lista todas las columnas de la tabla users y devuelve las primeras 2 líneas que encuentres.
> Nota: Se pueden cambiar por la cantidad de líneas que queramos ver.
---
## Joins (Relacionar tablas, conocido como Llaves Foraneas)
Tengo una tabla, la cual es:
```SQL
MariaDB [empresa]> SELECT * FROM users;
+----+---------+------+------------------+
| id | name | edad | email |
+----+---------+------+------------------+
| 1 | Juan | 25 | juan@juan.com |
| 2 | Alberto | 19 | alberto@juan.com |
| 3 | Leyre | 22 | Leyre@juan.com |
| 4 | Antonio | 52 | Antonio@juan.com |
| 5 | Ariel | 31 | Ariel@juan.com |
+----+---------+------+------------------+
```
Y quiero hacer un join de una tabla, la cual relacione él id de cada usuario con la creación de un producto en otra tabla llamada `products`. Se ha de hacer:
```SQL
CREATE TABLE products(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
created_by INT NOT NULL,
marca VARCHAR(50) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(created_by) REFERENCES `users`(id)
) ENGINE=InnoDB;
```
> En la parte de la llave foranea, describimos:
> La llave foranea será la columna `created_by` y su referencia se encuentra en la tabla `users` y la columna es `id`.
> Nota: Hay que usar BACKTICKS para seleccionar la tabla con nombres especiales, en este caso, `user` es un nombre especial en MariaDB. En una situación real, NO UTILICEIS `user` COMO NOMBRE DE TABLA. Tambien, el `ENGINE` es IMPORTANTE ponerlo. Sin `ENGINE`, no hay FK.
---
#### Insercion de multiples datos con un solo `INSERT`
Ahora, podemos añadir productos en la tabla products creadas por los usuarios de la tabla users:
```SQL
INSERT INTO products (name, created_by, marca)
VALUES
('ipad', 1, 'apple'),
('iphone', 1, 'apple'),
('watch', 2, 'apple'),
('macbook', 1, 'apple'),
('imac', 3, 'apple'),
('ipad mini', 2, 'apple');
```
> Inserta a la tabla productos dentro de las columnas name, created_by y marca...
Resultado:
```SQL
MariaDB [empresa]> SELECT * FROM products;
+----+-----------+------------+-------+
| id | name | created_by | marca |
+----+-----------+------------+-------+
| 1 | ipad | 1 | apple |
| 2 | iphone | 1 | apple |
| 3 | watch | 2 | apple |
| 4 | macbook | 1 | apple |
| 5 | imac | 3 | apple |
| 6 | ipad mini | 2 | apple |
+----+-----------+------------+-------+
```
---
## LEFT JOIN
Trae todos los registros dentro de la tabla de usuarios. En el caso de que se encuentren registros que se encuentren dentro de la tabla de productos que hayan sido creados por los usuarios que se encuentren en la tabla usuarios, aparecerán dentro de la consulta. En caso de que salgan usuarios que no hayan creado productos en la tabla de productos, simplemente márcamelos como `null`.
```SQL
SELECT u.id, u.email, p.name FROM users u LEFT JOIN products p ON u.id = p.created_by;
```
> Selecciona de la tabla de usuario, reconócelo con el alias `u`, y tráeme las columnas de id y email. Ahora, haz un LEFT JOIN con la tabla users como principal de producto, reconócelo como `p`, y tráeme la columna name y junta las columnas id de la tabla de usuario y created_by de la tabla de productos.
Resultado:
```SQL
+----+------------------+-----------+
| id | email | name |
+----+------------------+-----------+
| 1 | juan@juan.com | ipad |
| 1 | juan@juan.com | iphone |
| 2 | alberto@juan.com | watch |
| 1 | juan@juan.com | macbook |
| 3 | Leyre@juan.com | imac |
| 2 | alberto@juan.com | ipad mini |
| 4 | Antonio@juan.com | NULL |
| 5 | Ariel@juan.com | NULL |
+----+------------------+-----------+
```
---
## RIGHT JOIN
Mismo que `LEFT JOIN`, pero al revés.
Nos trae el producto y en caso de que exista, nos va a traer un usuario asociado.
```SQL
SELECT u.id, u.email, p.name FROM users u RIGHT JOIN products p ON u.id = p.created_by;
```
> Selecciona de la tabla de usuario, reconócelo con el alias `u`, y tráeme las columnas de id y email. Ahora, haz un RIGHT JOIN con la tabla de producto, reconócelo como `p`, y tráeme la columna name y junta las columnas id de la tabla de usuario y created_by de la tabla de productos.
Resultado:
```SQL
+------+------------------+-----------+
| id | email | name |
+------+------------------+-----------+
| 1 | juan@juan.com | ipad |
| 1 | juan@juan.com | iphone |
| 2 | alberto@juan.com | watch |
| 1 | juan@juan.com | macbook |
| 3 | Leyre@juan.com | imac |
| 2 | alberto@juan.com | ipad mini |
+------+------------------+-----------+
```
---
## INNER JOIN
Nos trae tanto usuarios como productos, pero siempre y cuando estos dos puedan ser asociados entre sí.
```SQL
SELECT u.id, u.email, p.name FROM users u INNER JOIN products p ON u.id = p.created_by;
```
> Selecciona de la tabla de usuario, reconócelo con el alias `u`, y tráeme las columnas de id y email. Ahora, haz un INNER JOIN con la tabla de producto, reconócelo como `p`, y tráeme la columna name y junta las columnas id de la tabla de usuario y created_by de la tabla de productos.
Resultado:
```SQL
+----+------------------+-----------+
| id | email | name |
+----+------------------+-----------+
| 1 | juan@juan.com | ipad |
| 1 | juan@juan.com | iphone |
| 2 | alberto@juan.com | watch |
| 1 | juan@juan.com | macbook |
| 3 | Leyre@juan.com | imac |
| 2 | alberto@juan.com | ipad mini |
+----+------------------+-----------+
```
---
## CROSS JOIN
Devuelve el producto cartesiano entre dos tablas.
Básicamente:
![[Mezcla-cartesiana_(CROSS_JOIN).png]]
```SQL
SELECT u.id, u.name, p.id, p.name FROM users u CROSS JOIN products p;
```
>Nota: No es recomendado hacerlo, y si se ha de hacerlo, hay que tener cuidado por la cantidad insana de datos que nos puede escupir. Puede bloquear la BD si los datos que escupen son grandes, así que, repito, MUCHO CUIDADO.
Resultado:
```SQL
+----+---------+----+-----------+
| id | name | id | name |
+----+---------+----+-----------+
| 1 | Juan | 1 | ipad |
| 2 | Alberto | 1 | ipad |
| 3 | Leyre | 1 | ipad |
| 4 | Antonio | 1 | ipad |
| 5 | Ariel | 1 | ipad |
| 1 | Juan | 2 | iphone |
| 2 | Alberto | 2 | iphone |
| 3 | Leyre | 2 | iphone |
| 4 | Antonio | 2 | iphone |
| 5 | Ariel | 2 | iphone |
| 1 | Juan | 3 | watch |
| 2 | Alberto | 3 | watch |
| 3 | Leyre | 3 | watch |
| 4 | Antonio | 3 | watch |
| 5 | Ariel | 3 | watch |
| 1 | Juan | 4 | macbook |
| 2 | Alberto | 4 | macbook |
| 3 | Leyre | 4 | macbook |
| 4 | Antonio | 4 | macbook |
| 5 | Ariel | 4 | macbook |
| 1 | Juan | 5 | imac |
| 2 | Alberto | 5 | imac |
| 3 | Leyre | 5 | imac |
| 4 | Antonio | 5 | imac |
| 5 | Ariel | 5 | imac |
| 1 | Juan | 6 | ipad mini |
| 2 | Alberto | 6 | ipad mini |
| 3 | Leyre | 6 | ipad mini |
| 4 | Antonio | 6 | ipad mini |
| 5 | Ariel | 6 | ipad mini |
+----+---------+----+-----------+
```
---
### Esquema de todos los JOIN:
- **LEFT**: Usuarios aunque no tengan producto.
- **INNER**: Solo relaciones validas.
- **RIGHT**: Casi nunca se usa en práctica.
---
## GROUP BY
Se utilizan con instrucciones que, por lo general, tienden a agrupar elementos.
```SQL
SELECT count(id), marca FROM products GROUP BY marca;
```
> Lista de la tabla products la cantidad de productos de las marcas que existan
Resultado:
```SQL
+-----------+-------+
| count(id) | marca |
+-----------+-------+
| 6 | apple |
+-----------+-------+
```
También podemos hacer lo mismo mezclándolo con un LEFT JOIN con la tabla de usuarios, donde la tabla primaria será la tabla de products:
```SQL
SELECT count(p.id), u.name FROM products p LEFT JOIN users u on u.id = p.created_by GROUP BY p.created_by;
```
Resultado:
```SQL
+-------------+---------+
| count(p.id) | name |
+-------------+---------+
| 3 | Juan |
| 2 | Alberto |
| 1 | Leyre |
+-------------+---------+
```
### `HAVING`
Podemos añadir condiciones. Por ejemplo, que el conteo sea mayor a 2:
```SQL
SELECT count(p.id), u.name FROM products p LEFT JOIN users u on u.id = p.created_by GROUP BY p.created_by HAVING count(p.id) >= 2;
```
Resultado:
```SQL
+-------------+---------+
| count(p.id) | name |
+-------------+---------+
| 3 | Juan |
| 2 | Alberto |
+-------------+---------+
```
---
## `DROP`
Sirve para eliminar tablas enteras.
```SQL
DROP TABLE products;
```
> Elimina la tabla de products.
> Nota: ¡CUIDADO CON ESTO! ¡LOS CAMBIOS SON IRREVERSIBLES!
---
## `ON DELETE` / `ON UPDATE`
Se utilizan en **llaves foráneas** para definir que ocurre cuando el registro referenciado cambia o se elimina.
```SQL
FOREIGN KEY (created_by) REFERENCES users(id)
ON DELETE <acción>
ON UPDATE <acción>
```
> Nota: Si NO se especifica nada, el comportamiento por defecto suele ser `RESTRICT`.
---
### `ON DELETE`
Define qué pasa cuando se elimina un registro de la tabla padre.
```SQL
ON DELETE RESTRICT --Restrict por defecto.
```
> No permite borrar el registro padre si hay hijos asociados. Es la opción más segura.
Ejemplo:
- No se puede borrar un usuario si tiene productos creados.
Uso recomendado:
- Datos críticos.
- Integridad estricta.
- Producción.
---
### `CASCADE`
Al borrar el padre, se borran automáticamente los hijos. Es peligroso si no sabes lo que estás haciendo. CUIDADO CON ESTO.
```SQL
ON DELETE CASCADE
```
Ejemplo:
- Borras un usuario -> Se borran todos sus productos.
Usos:
- Datos temporales
- Relaciones dependientes al 100%
>[!Warning]
>Un `DELETE` mal hecho PUEDE BORRAR MEDIA BASE DE DATOS.
---
### `SET NULL`
Al borrar el padre, la FK del hijo pasa a `null`. La columna **DEBE PERMITIR `NULL`**.
```SQL
ON DELETE SET NULL
```
Ejemplo:
- El producto sigue existiendo, pero ya no tiene creador.
Usos:
- Históricos
- Logs
- Datos que deben conservarse.
---
### `NO ACTION`
En MariaDB/MySQL se comporta igual que `RESTRICT` (aunque internamente SQL define `NO ACTION`). Existe por la compatibilidad con SQL.
```SQL
ON DELETE NO ACTION
```
Usos:
- En MariaDB, ninguno xd.
---
## `ON UPDATE`
Define qué pasa si cambia el valor de la **clave primaria referenciada**. Cambiar un PK en producción **NO es buena practica**, aunque exista el `ON UPDATE CASCADE`.
### `CASCADE`
Si cambia el `id` del padre, se actualiza el de los hijos. Es poco común, pero es correcto.
```SQL
ON UPDATE CASCADE
```
Ejemplo:
- Cambia el id de un usuario -> se actualiza en la tabla products.
---
### `RESTRICT`/`NO ACTION`
Impide cambiar la PK si hay relaciones.
```SQL
ON UPDATE RESTRICT
```
Uso:
- Lo normal en producción.
---
### `SET NULL`
Si cambia la PK, la FK pasa a NULL. Esto es muy raro de ver en la práctica.
```SQL
ON UPDATE SET NULL
```
---
## Ejemplo realista
```SQL
CREATE TABLE products (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
created_by INT NULL,
marca VARCHAR(50) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY (created_by) REFERENCES users(id)
ON DELETE SET NULL
ON UPDATE CASCADE
) ENGINE=InnoDB;
```
Explicación:
- Si se borra el usuario -> el producto se conserva
- Si cambia el id -> se actualiza automáticamente.
---
### Ejemplo ESTRICTO
```SQL
FOREIGN KEY (created_by) REFERENCES users(id)
ON DELETE RESTRICT
ON UPDATE RESTRICT
```
Explicación:
- En este caso, no se borra ni se cambia nada sin limpiar antes dependencias.
- Es lo ideal para los sistemas serios, como en producción.
---
## Resumen rápido
- `RESTRICT`: Seguridad
- `CASCADE`: Automatismo (peligroso)
- `SET NULL`: Conservar datos
- `NO ACTION`: Igual que `RESTRICT` en MariaDB
>[!note]
>Si no sabes qué usar, `RESTRICT`.
>Si usas `CASCADE`, es porque sabes lo que haces y lo has pensado dos veces.

View File

@@ -21,7 +21,10 @@ ON DELETE RESTRICT;
- `CASCADE`: - `CASCADE`:
```SQL ```SQL
ALTER TABLE Pedidos ALTER TABLE Pedidos
ADD CONSTRAINT fk_pedido_cliente ADD CONSTRAINT fk_pedido_cliente Programación PHP
Documento Padre
---
FOREIGN KEY (id_cliente) REFERENCES Clientes(id) FOREIGN KEY (id_cliente) REFERENCES Clientes(id)
ON DELETE CASCADE; ON DELETE CASCADE;
``` ```

View File

@@ -1,5 +0,0 @@
#SQL-ASIX - Structure Query Language
Proveniente de [[SQL]]
---
En SQL, los Procedimientos almacenados

View File

@@ -0,0 +1,86 @@
#SQL-ASIX - Structure Query Language
Proveniente de [[SQL]]
---
Un **trigger** o **disparador** es un objeto que se asocia con tablas y se almacenan en la base de datos. Su nombre se deriva por el comportamiento que presentan en su funcionamiento, ya que se ejecutan cuando sucede algún evento sobre las tablas a las que se encuentra asociado. Los eventos que hacen que se ejecute un trigger son las operaciones de inserción (`INSERT`), borrado (`DELETE`), o actualización (`UPDATE`), ya que modifican los datos de una tabla.
La utilidad principal de un trigger es mejorar la gestión de la base de datos, ya que no requieren que un usuario los ejecute. Por lo tanto, son empleados para implementar las [[Reglas de negocio|reglas de negocio]] (tipo especial de integridad) de una base de datos. Una regla de negocio es cualquier restricción, requerimiento, necesidad o actividad especial que debe ser verificada al momento de intentar agregar, borrar o actualizar la información de una base de datos. Los triggers pueden prevenir errores en los datos, modificar valores de una lista, sincronizar tablas, entre otros.
---
## Combinaciones
La acción del trigger, siempre que no se viole la restricción del trigger se ejecuta dependiendo de la combinación de tipos de trigger:
- **Before statement**: Antes de ejecutar el disparo.
- **Before row**: Antes de modificar cada fila afectada por la sentencia de disparo y antes de chequear las restricciones de integridad apropiadas.
- **After statement**: Después de ejecutar la sentencia del disparo y después de chequear las restricciones de integridad apropiadas.
- **After row**: Después de modificar cada fila afectada por la sentencia del disparo y posiblemente aplicando las restricciones de integridad apropiadas.
---
## Componentes principales
Estructura básica de un "trigger":
- **Llamada de activación**: Es la sentencia que permite "disparar" el código a ejecutar.
- **Restricción**: Es la condición necesaria para realizar el código. Esta restricción puede ser de tipo condicional o de tipo nulidad.
- **Accón a ejecutar**: Es la secuencia de instrucciones a ejecutar una vez que se han cumplido las condiciones iniciales.
---
## Tipos
Existen dos tipos de disparadores que se clasifican según la cantidad de ejecuciones a realizar:
- ***Row Triggers*** (o Disparadores de fila): Son aquellas que se ejecutaran cada vez que se llama al disparador desde la tabla asociada al trigger.
- ***Statement Triggers*** (o Disparadores de secuencia): Son aquellos que, sin importar la cantidad de veces que se cumpla con la condición, su ejecución es única.
Pueden ser de sesión y almacenados; pero no son recomendables.
---
## Efectos y características
- No aceptan parámetros o argumentos (pero podrían almacenar los datos afectados en tablas temporales).
- No pueden ejecutar las operaciones ***COMMIT*** o ***ROLLBACK*** porque estas son parte de la sentencia SQL del disparador (únicamente a través de transacciones autónomas).
- Pueden causar errores de mutaciones en las tablas, si se han escrito de manera deficiente.
---
## Ejemplos
Ejemplo sencillo para SQL Server sería crear un ***Trigger*** para insertar un pedido de algún producto cuando la cantidad de este, en nuestro almacén, sea inferior a un valor dado.
```SQL
CREATE TRIGGER TR_ARTICULO
ON ARTICULOS
AFTER UPDATE
AS
BEGIN
INSERT INTO HCO_ARTICULO
(IDARTICULO, STOCK, FECHA)
SELECT ID_ARTICULO, STOCK, GETDATE()
FROM INSERTED
END
INSERT INTO ARTICULOS VALUES (1, 'MEMORIA', 12, '12/03/2015')
SELECT * FROM ARTICULOS
UPDATE ARTICULOS
SET STOCK = STOCK -20
WHERE ID_ARTICULO = 1
SELECT * FROM HCO_ARTICULO
```
---
## Disparadores en MySQL
Los disparadores son soportados en MySQL a partir de la versión 5.0.2. Algunos de los soportes existentes son los disparadores para las sentencias *INSERT, UPDATE y DELETE*.
El estándar SQL:2003 requiere que los disparadores den a los programadores acceso a las variables de un registro utilizando una sintaxis como *REFERENCING NEW AS n*. Por ejemplo, si un disparador está monitoreando los cambios en la columna *salario*, podría escribirse un disparador como:
```SQL
CREATE TRIGGER ver_salario
BEFORE UPDATE ON empleados
REFERENCING NEW ROW AS n, OLD ROW AS o
FOR EACH ROW
IF n.salario <> o.salario THEN
END IF;
```
Como en MySQL las sentencies se ejecutan luego de escribir el signo punto y coma (;), cabe destacar que para crear un disparador en MySQL, antes se escribe la sentencia *DELIMITER* seguida de un carácter tal como |, la cual asigna la función del punto y coma a otro carácter permitiendo que el disparador sea escrito usando los puntos y comas sin que se ejecute mientras se escribe; después de escrito el disparador se escribe nuevamente a la sentencia ***DELIMITER;***, para asignar al punto y coma su función habitual.
---
##### Enlaces externos
- [Trigger (base de datos) - Wikipedia, la enciclopedia libre](https://es.wikipedia.org/wiki/Trigger_(base_de_datos))

View File

@@ -0,0 +1,32 @@
#SQL-ASIX - Structure Query Language
Proveniente de [[SQL]]
---
En SQL, un **procedimiento almacenado (*stored procedure*** en inglés) es un programa o procedimiento almacenado físicamente en una base de datos. Su implementación varía de un gestor de base de datos a otro. **La ventaja de un procedimiento almacenado** es que al ser ejecutado, en respuesta a una petición del usuario, es ejecutado directamente en el motor de base de datos, el cual usualmente corre en un servidor separado. Como tal, posee acceso directo a los datos que necesita manipular y solo necesita enviar grandes cantidades de datos salientes y entrantes.
Los procedimientos pueden ser ventajosos: cuando una base de datos es manipulada desde muchos programas externos. Al incluir una lógica de aplicación en la base de datos utilizando procedimientos almacenados, la necesidad de embeber la misma lógica en todos los programas que acceden a los datos es reducida. Esto puede simplificar la creación y, particularmente, el mantenimiento de los programas involucrados.
Podemos ver un claro ejemplo de estos procedimientos cuando requerimos realizar una misma operación en un servidor dentro de algunas o todas las bases de datos y a la vez dentro de todas o algunas de las tablas de las bases de datos al mismo tiempo. Para ello podemos utilizar a los procedimientos almacenados auto creables que es una forma de generar ciclos redundantes a través de los procedimientos almacenados.
## Implementación
---
Estos procedimientos se usan a menudo, pero no siempre, para realizar consultas [[SQL]] sobre los objetos de la base de datos de una manera abstracta, desde el punto de vista del cliente de la aplicación. Un **procedimiento almacenado** permite agrupar en forma exclusiva parte de algo específico que se desee realizar o, mejor dicho, el SQL apropiado para dicha acción.
## Usos
---
Los usos típicos de los **procedimientos almacenados** se aplican en la validación de datos, integrados dentro de la estructura del banco de datos. Los **procedimientos almacenados** usados con tal propósito se llaman comúnmente [[Disparadores|disparadores, o triggers]]. Otro uso común es la 'encapsulación' de una API para un proceso complejo o grande que podría requerir la 'ejecución' de varias consultas SQL, tales como la manipulación de un conjunto de datos enorme para producir un resultado resumido.
También pueden ser usados para el control de gestión de operaciones, y ejecutar procedimientos almacenados dentro de una transición, de tal manera que las transiciones sean efectivamente transparentes para ellos.
## Ventajas
---
La ventaja de un **procedimiento almacenado**, en respuesta a una petición de usuario, está directamente bajo el control del motor del gestor de bases de datos, que corre generalmente en un servidor distinto del servidor web, aumentando con ello la rapidez de procesamiento de las peticiones del usuario. El servidor de la base de datos tiene acceso directo a los datos necesarios para manipular y solo necesita enviar el resultado final al usuario. Los datos, que pueden simplificar la gestión de datos y reducir la necesidad de codificar la lógica en el resto de los programas cliente. Esto puede reducir la probabilidad de que los datos se corrompan por el uso de programas clientes defectuosos o erróneos. De este modo, el motor de base de datos puede asegurar la integridad de los datos y su consistencia con la ayuda de procedimientos almacenados. Algunos afirman que las bases de datos deben ser utilizadas para el almacenamiento de datos solamente, y que la lógica de negocio solo debería aplicarse en la capa de negocio del código, a través de aplicaciones cliente que deban acceder a los datos. Sin embargo, el uso de procedimientos almacenados no se opone a la utilización de una capa de negocio.
El siguiente es un ejemplo de procedimiento almacenado en MySQL:
```SQL
CREATE PROCEDURE nombreProcedimiento([parametro1, parametro2,...])
[Atributos]
BEGIN Instrucciones
END
```

View File

@@ -1,4 +1,4 @@
#SQL-ASIX - Structure Query Language #SQL-ASIX - Structured Query Language
Proveniente de [[SQL]] Proveniente de [[SQL]]
--- ---
@@ -8,16 +8,21 @@ Algunos de los tipos de datos basicos de SQL son:
--- ---
## Numeros enteros: ## Numeros enteros:
- **TINYINT(Tamaño)**: -128 a 127 normal. 0 a 255 sin signo. La cantidad maxima de digitos se puede especificar entre parentesis. - **TINYINT**: -128 a 127 normal. 0 a 255 sin signo.
- **SMALLINT(Tamaño)**: -32768 a 32767 normal. 0 a 65535 sin signo. La cantidad maxima de digitos se pueden especificar entre parentesis. - **SMALLINT**: -32768 a 32767 normal. 0 a 65535 sin signo.
- **MEDIUMINT(Tamaño)**: -8388608 a 8388607 normal. 0 a 16777215 sin signo. La cantidad maxima de digitos se pueden especificar entre parentesis. - **MEDIUMINT**: -8388608 a 8388607 normal. 0 a 16777215 sin signo.
- **INT(Tamaño)**: -2147483648 a 2147483647 normal. 0 a 4294967295 sin signo. La cantidad maxima de digitos se pueden especificar entre parentesis. - **INT**: Da al valor el tipo INTEGER. Entero de 32 bits. Rango: -2147483648 a 2147483647 (signed)
- **BIGINT(Tamaño)**: -9223372036854775808 a 9223372036854775807 normal. 0 a 18446744073709551615 sin signo. La cantidad maxima de digitos se pueden especificar entre parentesis. - **BIGINT**: -9223372036854775808 a 9223372036854775807 normal. 0 a 18446744073709551615 sin signo.
---
## Numeros en punto flotante ## Numeros en punto flotante
- **FLOAT(tamaño, d)**: Un pequeño numero con un punto decimal flotante. La cantidad maxima de digitos puede especificar en el parametro de tamaño. El numero maximo de digitos a la derecha del punto decimal se especifica en el parametro d. - **FLOAT(tamaño, d)**: Un pequeño numero con un punto decimal flotante. La cantidad maxima de digitos puede especificar en el parametro de tamaño. El numero maximo de digitos a la derecha del punto decimal se especifica en el parametro d.
- **DECIMAL(tamaño, d)**: Un DOBLE almacenado como una cadena, lo que permite un punto decimal fijo. La cantidad maxima de digitos se puede especificar en el parametro de tamaño. El numero maximo de digitos a la derecha del punto decimal se especifica en el parametro d. - **DECIMAL(tamaño, d)**: Un número exacto almacenado internamente en formato decimal fijo. La cantidad maxima de digitos se puede especificar en el parametro de tamaño. El numero maximo de digitos a la derecha del punto decimal se especifica en el parametro d.
>[!bug]
>FLOAT es un tipo aproximado y puede introducir errores de precisión.
---
## Fechas y tiempos ## Fechas y tiempos
- **DATE ()**: Una fecha. Formato: AAAA-MM-DD. - **DATE ()**: Una fecha. Formato: AAAA-MM-DD.
- **DATETIME ()**: Una combinacion de fecha y hora. Formato: AAAA-MM-DD HH:MI:SS. - **DATETIME ()**: Una combinacion de fecha y hora. Formato: AAAA-MM-DD HH:MI:SS.
@@ -32,10 +37,10 @@ Algunos de los tipos de datos basicos de SQL son:
> Para TIME, el rango admitido es de '-838:59:59' a '838:59:59' > Para TIME, el rango admitido es de '-838:59:59' a '838:59:59'
> Para YEAR, los valores permitidos van en 4 digitos: de 1901 a 2155. Valores permitidos en formato de dos digitos: 70 a 69, que representan los años de 1970 a 2069. > Para YEAR, los valores permitidos van en 4 digitos: de 1901 a 2155. Valores permitidos en formato de dos digitos: 70 a 69, que representan los años de 1970 a 2069.
---
## Cadena de caracteres: ## Cadena de caracteres:
- **CHAR (Tamaño)**: Cadena de longitud fija (puede contener letras, numeros y caracteres especiales), El tamaño fijo se especifica entre parentesis. Puede almacenar hasta 255 caracteres. - **CHAR (Tamaño)**: Cadena de longitud fija (puede contener letras, numeros y caracteres especiales), El tamaño fijo se especifica entre parentesis. Puede almacenar hasta 255 caracteres.
- **VARCHAR(Tamaño)**: Tiene una cadena de longitud variable (puede contener letras, numeros y caracteres especiales). El tamaño maximo se especifica entre parentesis. Puede almacenar hasta 255 caracteres. - **VARCHAR(Tamaño)**: Tiene una cadena de longitud variable (puede contener letras, numeros y caracteres especiales). El tamaño maximo se especifica entre parentesis. Puede almacenar hasta 255 caracteres dependiendo del charset con `utf8mb4` puede almacenar hasta 65535 bytes (~16383 caracteres), aunque 255 es una práctica común.
- **TINYTEXT**: Tiene una cadena con una longitud maxima de 255 caracteres - **TINYTEXT**: Tiene una cadena con una longitud maxima de 255 caracteres
- **TEXTO**: Tiene una cadena con una longitud maxima de 65535 caracteres. - **TEXTO**: Tiene una cadena con una longitud maxima de 65535 caracteres.
- **BLOB**: Para BLOB (Objetos grandes binarios). Almacena hasta 65535 bytes de datos. - **BLOB**: Para BLOB (Objetos grandes binarios). Almacena hasta 65535 bytes de datos.
@@ -45,12 +50,16 @@ Algunos de los tipos de datos basicos de SQL son:
- **LONGBLOB**: Para BLOB (Objetos grandes binarios). Tiene una capacidad para 4294967295 bytes de datos. - **LONGBLOB**: Para BLOB (Objetos grandes binarios). Tiene una capacidad para 4294967295 bytes de datos.
>[!bug] >[!bug]
> Nota: Si, en VARCHAR, agregas un valor mas grande que 255, este se convertira en tipo TEXTO. > Nota: Si, en VARCHAR, agregas un valor mas grande que 255, MySQL rechaza el INSERT.
---
## ENUM y SET ## ENUM y SET
- **Enum (x, y, z, etc.)**: Permite ingresar una lista de valores posibles. Puede enumerar hasta 65535 valores en una lista ENUM. Si se inserta un valor que no esta en la lista, se insertará un valor en blanco. - **Enum (x, y, z, etc.)**: Permite ingresar una lista de valores posibles. Puede enumerar hasta 65535 valores en una lista ENUM. Inserta el primer valor del ENUM, o `''` si existe.
- **Set**: Similar a Enum, excepto que SET puede contener hasta 64 elementos de una lista y puede almacenar mas de opcion - **Set**: Similar a Enum, excepto que SET puede contener hasta 64 elementos de una lista y puede almacenar mas de opcion
>[!bug]
>Nota: Si se inserta un valor no permitido, MariaDB inserta el primer valor del ENUM o una cadena vacía y lanza un warning.
---
## Binarios: ## Binarios:
- **bit** Entero que puede ser 0, 1 o NULL. - **bit**: Tipo binario para almacenar Bits. No equivalente a BOOLEAN. Poco usado.

View File

@@ -33,9 +33,91 @@ if ! grep -q "hola" archivos.txt; then
fi fi
``` ```
---
## `case...in...esac`
```Shell
case $option in
start) echo "iniciando";;
stop) echo "Finalizando";;
*) echo "Opción no válida";;
esac
```
---
## `for, in, do, done` (Bucles)
```Shell
for i in 1 2 3; do
echo "numero $i"
done
```
---
## `while, until` (Bucles condicionales)
```Shell
while [[ "$x" -lt 5 ]]; do
echo $x
((x++))
done
```
```Shell
until [[ "$x" -eq 5 ]]; do
echo $x
((x++))
done
```
---
## `select` (Menú interactivo)
```Shell
select opcion in Start Stop Exit; do
echo "Elegiste la opcion $opcion"
done
```
---
## Contador de longitud
```Shell
validador() {
local passwd=$1
local long=${#passwd} # Para leer longitud de un dato, se usa ${#variable}
if [[ "$long" -gt 8 ]]; then
echo "PWD_GT_8"
else
echo "PWD_LT_8"
fi
}
```
---
### Verificación de mayúsculas, minúsculas, números y caracteres especiales
Se utiliza el comparador de valores regex (`=~`) en conjunto con los valores regex:
#### ¿Minúscula?
```Shell
[[ "Hola123" =~ [a-z] ]] # true
```
##### ¿Mayúscula?
```Shell
[[ "Hola123" =~ [A-Z] ]] # true
```
##### ¿Números?
```Shell
[[ "abc123def" =~ [0-9] ]] # true
```
###### ¿Caracteres especiales?
```Shell
[[ "asd1!213das" =~ [^a-zA-Z0-9] ]] # true
```

View File

@@ -69,3 +69,205 @@ Bibliografía Web:
- [Manejo de errores en Bash Scripting](https://adictosalinux.com/manejo-errores-scripts-bash/) - [Manejo de errores en Bash Scripting](https://adictosalinux.com/manejo-errores-scripts-bash/)
- [Comparator operators](https://www.tutorialkart.com/bash-shell-scripting/bash-comparison-operators/) - [Comparator operators](https://www.tutorialkart.com/bash-shell-scripting/bash-comparison-operators/)
- [Funciones Bash](https://itsfoss.com/es/funciones-bash/) - [Funciones Bash](https://itsfoss.com/es/funciones-bash/)
---
# 2. Validador de contraseñas
Entrada: Una contraseña.
Objetivo: Que detecte la longitud y cantidad de caracteres especiales y dará el diagnóstico `weak`, `mid` y `strong`.
```Shell
#!/bin/bash
# Funciones Puras
# En caso de muchos condicionales
# Recurrir a negaciones
longitud() {
local passwd=$1
local long=${#passwd}
if [[ "$long" -gt 8 ]]; then
echo "PWD_GT_8"
else
echo "PWD_LT_8"
fi
}
minusculas() {
local passwd=$1
if [[ "$passwd" =~ [a-z] ]]; then
echo "MINLETTERS"
else
echo "NO_MINLETTERS"
fi
}
mayusculas () {
local passwd=$1
if [[ "$passwd" =~ [A-Z] ]]; then
echo "MAYLETTERS"
else
echo "NO_MAYLETTERS"
fi
}
numeros () {
local passwd=$1
if [[ "$passwd" =~ [0-9] ]]; then
echo "NUMBERS"
else
echo "NO_NUMBERS"
fi
}
caracteresE () {
local passwd=$1
if [[ "$passwd" =~ [^a-zA-Z0-9] ]]; then #Caso curioso: Se recurre a la negacion, ya que si existe un
#caracter especial, este no estará en las tablas de mayuscula, minuscula ni numeros.
echo "ECARACTERS"
else
echo "NO_ECARACTERS"
fi
}
validador () {
local passwd=$1
local long=$(longitud "$passwd")
local min=$(minusculas "$passwd")
local may=$(mayusculas "$passwd")
local num=$(numeros "$passwd")
local carE=$(caracteresE "$passwd")
res=("$long" "$min" "$may" "$num" "$carE")
local puntuacion=0
local totcriterios=5
for i in "${res[@]}"; do
if [[ $i = "PWD_GT_8" || $i = "MINLETTERS" || $i = "MAYLETTERS" || $i = "NUMBERS" || $i = "ECARACTERS" ]]; then
((puntuacion++)) # Esto tiene un error de sintaxis aritmetica: Se esperaba un operando (el elemento de error es "+"). El error sale cuando no se introduce uno de los valores anteriores, como por ejemplo, no poner ningun simbolo.
#Update: EUREKA! Al poner $, bash expande el valor antes de la operacion aritmetica.
#Si la puntuacion vale 0, bash intenta ejecutar ((0++)).
fi
done
((porcentaje=puntuacion * 100 / totcriterios))
if [[ "$porcentaje" -lt 41 ]]; then
echo "Weak"
elif [[ "$porcentaje" -lt 71 ]]; then
echo "Mid"
else
echo "Strong"
fi
}
read -p "Introduce una contraseña: " a
resultado=$(validador "$a" )
```
Bibliografía Web:
- [Conversión de mayúsculas a minúsculas](https://es.stackoverflow.com/questions/346567/cómo-convierto-un-texto-a-mayúsculas-o-minúsculas-en-bash-zsh-o-osx)
- [Using && and || in bash](https://kyleshevlin.com/using-and-and-or-in-bash-scripts/)
- [Bash operators](https://www.w3schools.com/bash/bash_operators.php)
# 3. Normalizador de nombres de archivos
---
Objetivo: Tener un nombre de archivo asqueroso y limpiarlo.
##### Ejemplo de entradas:
```Shell
" Hola Mundo.txt"
"Mi fichero (copia) (1).png"
```
- `normalizar_nombre` - Elimina espacios extra, pasa a minúsculas, reemplazo de espacios con `_`.
# 4. Parser de configuración estilo .ini
---
Objetivo: Procesar una línea. No se toca ficheros en la función pura.
##### Funciones puras:
- `es_comentario`
- `es_seccion`
- `obtener_clave`
- `obtener_valor`
##### Parte impura:
- Leer el archivo línea por línea
- Imprimir errores o configuraciones
Se entrena el patron: *la funcion pura opera en una linea, la impura itera por el fichero*.
# 5. Mini motor de templates
---
Objetivo: Reemplazo de variables del estilo:
```Shell
Hola {{NOMBRE}}
```
##### Funcion pura
- `renderizar_template "$template" "$nombre"`
Debe sustituir `{{NOMBRE}}` por el valor que le pasas.
##### Parte impura:
- Leer `.template`
- Guardar la salida en un fichero final.
Es composicion pura de texto, sin tocar nada externo.
# 6. Filtro de logs
---
Objetivo: Se procesa cada linea para extraer informacion util
##### Funciones puras
- `es_error`
- `extraer_fecha`
- `extraer_mensaje`
- `formato_limpio`
##### Parte impura
- Leer fichero de log gigante
- Guardar resultados filtrados
Procesamiento funcional de toda la vida. Muy util.
# 7. Comparador de versiones
---
Ejemplo:
```Shell
compare_versions "1.2.10" "1.3.1"
```
##### Salida pura:
- `-1` si v1 < `v2`
- `0` si iguales
- `1` si `v1` > `v2`
##### Parte impura
- Nada mas que capturar valores y mostrar los resultados.
Perfecto para practicar manejo de arrays y logica sin tocar sistema.
# 8. Generador de checksums "puro"
---
No se calcula archivos, solo texto.
##### Funciones puras
- `checksum_simple "$texto"` - Ejemplo: suma ASCII mod 10000
- `hash_basico "$texto"`
##### Parte impura
- Leer ficheros desde disco
- Pasar su contenido a la parte pura
Obliga a pensar que es pureza y que no.
# 9. Conversor de fecha
---
Convierte formatos
Entrada:
```shell
2025-12-10
```
Salida de funcion pura:
```shell
10/12/2025
```
##### Impuro
Nada fuera de imprimir resultados
Es un ejemplo simple para disciplina.
# 10. Mini-linter para Bash
---
Suena más grande de lo que es.
##### Funcion pura
- `analizar_linea "$linea"` - Devuelve etiquetas como `"echo esta mal"`, `"mal identado"`, `"ok"`.
###### Funcion impura
- Leer archivo `.sh`
- Contar errores
- Mostrar resumen
Mola detectar mierda en los scripts. Tambien fuerza a separar analisis puro de acciones impuras.

View File

@@ -0,0 +1,81 @@
#!/bin/bash
# Funciones Puras
# En caso de muchos condicionales
# Recurrir a negaciones
longitud() {
local passwd=$1
local long=${#passwd}
if [[ "$long" -gt 8 ]]; then
echo "PWD_GT_8"
else
echo "PWD_LT_8"
fi
}
minusculas() {
local passwd=$1
if [[ "$passwd" =~ [a-z] ]]; then
echo "MINLETTERS"
else
echo "NO_MINLETTERS"
fi
}
mayusculas () {
local passwd=$1
if [[ "$passwd" =~ [A-Z] ]]; then
echo "MAYLETTERS"
else
echo "NO_MAYLETTERS"
fi
}
numeros () {
local passwd=$1
if [[ "$passwd" =~ [0-9] ]]; then
echo "NUMBERS"
else
echo "NO_NUMBERS"
fi
}
caracteresE () {
local passwd=$1
if [[ "$passwd" =~ [^a-zA-Z0-9] ]]; then #Caso curioso: Se recurre a la negacion, ya que si existe un
#caracter especial, este no estará en las tablas de mayuscula, minuscula ni numeros.
echo "ECARACTERS"
else
echo "NO_ECARACTERS"
fi
}
validador () {
local passwd=$1
local long=$(longitud "$passwd")
local min=$(minusculas "$passwd")
local may=$(mayusculas "$passwd")
local num=$(numeros "$passwd")
local carE=$(caracteresE "$passwd")
res=("$long" "$min" "$may" "$num" "$carE")
local puntuacion=0
local totcriterios=5
for i in "${res[@]}"; do
if [[ $i = "PWD_GT_8" || $i = "MINLETTERS" || $i = "MAYLETTERS" || $i = "NUMBERS" || $i = "ECARACTERS" ]]; then
((puntuacion++)) # Esto tiene un error de sintaxis aritmetica: Se esperaba un operando (el elemento de error es "+"). El error sale cuando no se introduce uno de los valores anteriores, como por ejemplo, no poner ningun simbolo.
#Update: EUREKA! Al poner $, bash expande el valor antes de la operacion aritmetica.
#Si la puntuacion vale 0, bash intenta ejecutar ((0++)).
fi
done
((porcentaje=puntuacion * 100 / totcriterios))
if [[ "$porcentaje" -lt 41 ]]; then
echo "Weak"
elif [[ "$porcentaje" -lt 71 ]]; then
echo "Mid"
else
echo "Strong"
fi
}
read -p "Introduce una contraseña: " a
resultado=$(validador "$a" )
echo "Su contraseña es $resultado"

View File

@@ -125,3 +125,4 @@ Tal vez pienses que es más trabajo, pero los beneficios son enormes:
1. **La depuración es más sencilla**: Si algo, por alguna razón, falla, sabes que el error está *dentro* de ella o en los argumentos que recibió. No tienes que rastrear variables globales por todo el fichero. 1. **La depuración es más sencilla**: Si algo, por alguna razón, falla, sabes que el error está *dentro* de ella o en los argumentos que recibió. No tienes que rastrear variables globales por todo el fichero.
2. **Reutilización**: Puedes hacer *copy-paste* de una función pura en otro script y funcionará inmediatamente sin romper nada (en la gran mayoría de casos). 2. **Reutilización**: Puedes hacer *copy-paste* de una función pura en otro script y funcionará inmediatamente sin romper nada (en la gran mayoría de casos).
3. **Seguridad**: Evita las colisiones de nombres de variables (el típico error donde dos bucles usan la variable `i` y uno rompe al otro). 3. **Seguridad**: Evita las colisiones de nombres de variables (el típico error donde dos bucles usan la variable `i` y uno rompe al otro).

View File

@@ -0,0 +1,114 @@
#LilCMS-JS
LilCMS.JS es, como su nombre indica, un CMS de creación propia basado en node.js y JavaScript. Es un sistema de renderizado en cliente (*Client-Side Rendering*). El repositorio original es este:
### - [LilCMS - A personal-made CMS](https://github.com/Lil-Carpi/LilCMS)
### TENED EN CUENTA:
- La versión original estará hecha en `PHP`, un lenguaje deprogramaciónn queaúnn no controlo.
El repositorio de ESTE proyecto será
### - [LilCMS.JS - A personal-made CMS, but on JavaScript](https://github.com/Lil-Carpi/LilCMS.js)
La idea es simple: Crear un Gestor de contenidos simple, útil e intuitivo, en el cual se puedan definir diferentes elementos (web components) para cada cosa, como footer, content, headers, navbar, etc.
El motor de base de datos para la gestión de usuarios será MariaDB.
Estructura de ficheros (Base):
```
.
├── assets
│   ├── css
│   ├── fonts
│   ├── img
│   └── js
│   └── web-component.js
├── favicon.ico
├── index.html
├── main
│   └── components
│   ├── content.html
│   └── footer.html
└── private
```
- **/assets**: Directorio contenedor de CSS, fuentes, imágenes a ser necesario y ficheros JS.
- **/main**: Directorio contenedor de componentes de la página web. Estos sirven para ser insertados mediante web-component.js al index.html principal.
- **/private**: Directorio contenedor experimental. Probablemente segurizado mediante identificación por servidor SQL.
---
## Contenido importante por el momento:
### `/index.html`
```HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="/assets/js/web-component.js"></script>
</head>
<body>
<h1>Desde index</h1>
<mi-content></mi-content>
<mi-footer></mi-footer>
</body>
</html>
```
### `/main/components/content.html`
```HTML
<h1>Contenido</h1>
<ul>
<li>
<p>Poco texto</p>
<p>Mucho texto</p>
<p>Medio texto</p>
<p>Completo texto</p>
<p>Final de texto</p>
</li>
</ul>
```
### `/main/components/footer.html`
```HTML
<h1>Contenido</h1>
<ul>
<li>
<p>Poco texto</p>
<p>Mucho texto</p>
<p>Medio texto</p>
<p>Completo texto</p>
<p>Final de texto</p>
</li>
</ul>
```
### `/assets/js/web-components.js`
```JavaScript
class MiFooter extends HTMLElement {
connectedCallback() {
fetch('../main/components/footer.html')
.then(r => r.text())
.then(html => this.innerHTML = html);
}
}
customElements.define('mi-footer', MiFooter);
class MiContent extends HTMLElement {
connectedCallback() {
fetch('../main/components/content.html')
.then(r => r.text())
.then(html => this.innerHTML = html);
}
}
customElements.define('mi-content', MiContent);
```

View File

@@ -0,0 +1,13 @@
#LilCMS-JS
En esta versión, he conseguido crear el back-end con `node.js`. En un principio, he conseguido hacerlo funcionar mediante web components HTML, Después he conseguido conectar una base de datos externa al back-end para que interactúe con el usuario admin, dando el siguiente resultado:
```JSON
{"mensaje":"¡Conexión exitosa con MariaDB!",
"datos":
[{"id":1,
"username":"admin",
"password":"1234",
"role":"admin",
"created_at":"2025-12-15T22:56:03.000Z"}]}
```
Así que, podemos concluir, que la versión 0.0.1 está terminada.

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -0,0 +1,4 @@
#LDM-CSS - CSS
Documento Padre
---

View File

@@ -0,0 +1,4 @@
#LDM-HTML - HTML
Documento Padre
---

View File

@@ -0,0 +1,6 @@
#LDM-JavaScript - Programación JavaScript
Documento Padre
---
### Documento Padre.

View File

@@ -0,0 +1,7 @@
#M9-PHP - Programación PHP
Documento Padre
---
### Documento padre de PHP
Documentos Hijos:

View File

@@ -0,0 +1,8 @@
#Python Programación Python
Documento Padre
---
### Documento padre de Python.
Documentos hijos:

View File

@@ -73,4 +73,3 @@ iOS tampoco deja clonar repos... Ni hacer nada en general, usa la copia en Andro
--- ---
Abrís Obsidian y buscáis donde hayáis descargado el repo y listo. Abrís Obsidian y buscáis donde hayáis descargado el repo y listo.
(No me lo confundas con el juego. O sí, pero luego no digas que no furula).