Coautor: Sitaram Vemulapalli, Ingeniero Principal, Couchbase R&D.
"La respuesta, amigo mío, se esconde en JSON" - Bob Dylan
Hay muchos público conjuntos de datos JSON y luego se impresionante Conjuntos de datos JSON. Todas las empresas, incluida la suya, han almacenado muchos datos en JSON: el resultado de encuestas, campañas y foros.
Hay muchas maneras de conseguir la piel del JSON. Usted puede escribir el programa Python para cada informe, la visualización que desea hacer. O bien, puede utilizar N1QL (SQL para JSON) a generar el algoritmo adecuado para usted para analizar datos JSON. En este artículo, le mostramos cómo utilizar N1QL para extraer información rápidamente. También utilizamos dos funciones que aparecerán en la próxima versión: Common Table Expression (CTE) y Window Functions.
Objetivo: Utilizar el conjunto de datos JSON público de los resultados del US Open de golf para crear una tabla de clasificación simple, un ranking, etc.
Tres cosas que harás como parte de esto:
- Ingerir los datos en Couchbase fácilmente.
- Comienza a obtener el valor de estos datos JSON inmediatamente.
- Dé forma al JSON para generar rápidamente informes útiles utilizando las nuevas funciones.
Fuente de datos: https://github.com/jackschultz/usopen
Las consultas de este post también están disponibles en: https://github.com/keshavmr/usopen-golf-queries
Estructura del repositorio de datos: Este repositorio de GitHub https://github.com/jackschultz/usopen contiene US Open golf -2018 datos. Para cada hoyo, como un documento separado para cada día.
Cada documento tiene esta estructura. Este es el documento para el hoyo 1 del día 1. La Ps archivada tiene la lista de jugadores, cada uno con un ID único.
A continuación se presentan las estadísticas de juego de cada jugador, golpe a golpe. Los jugadores se emparejan con las puntuaciones utilizando el campo ID único para el jugador.
Empieza a obtener información:
Antes de iniciar la consulta, cree un índice primario en el bucket.
CREAR ÍNDICE PRIMARIO EN usopen;
Tarea 1: Crea un informe con las puntuaciones de los jugadores por rondas y el total final.
Después de jugar con JSON de abajo hacia arriba, llegamos a esta consulta. La explicación está después de la consulta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
CON d AS ( SELECCIONE pl.hnum AS holedn, pl.ps.Nat AS país, (pl.ps.FN || " " || pl.ps.LN) AS nombre, pl.ps.ID AS ID, longitud_array(hps.Sks) AS puntuación, hpl.agujero AS `agujero`, hpl.día AS `día` DESDE ( SELECCIONE meta(usopen).id AS hnum, ps DESDE usopen UTILICE llaves "agujeros:1:1" sin anestesia Ps AS ps ) pl INTERIOR ÚNASE A ( SELECCIONE TONÚMERO(dividir(meta(usopen).id, ":") [1]) AS `agujero`, TONÚMERO(dividir(meta(usopen).id, ":") [2]) AS `día`, hps DESDE usopen sin anestesia Rs AS rs UNNEST rs.Hs AS hs UNNEST hs.HPs AS hps ) hpl EN (pl.ps.ID = hps.ID) ) SELECCIONE d.nombre, SUM( CASO CUANDO d.día = 1 ENTONCES d.puntuación ELSE 0 FIN ) R1, SUM( CASO CUANDO d.día = 2 ENTONCES d.puntuación ELSE 0 FIN ) R2, SUM( CASO CUANDO d.día = 3 ENTONCES d.puntuación ELSE 0 FIN ) R3, SUM( CASO CUANDO d.día = 4 ENTONCES d.puntuación ELSE 0 FIN ) R4, SUM(d.puntuación) T DESDE d GRUPO POR d.nombre PEDIR POR d.nombre |
Resultados tabulares (en forma tabular, desde el banco de trabajo de consultas de Couchbase)
Veamos la consulta bloque por bloque.
Mira la cláusula WITH d. La sentencia desenreda el JSON de datos PER-día-PER-agujero-disparo-a-disparo a simples valores escalares.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "d": { "ID": "37189", "país": "USA", "día": 1, "agujero": 10, "holedn": "agujeros:1:1", "nombre": "Harold Varner", "puntuación": 6 } } |
Holedn es la clave del documento - hole-day-number
País es la nacionalidad del jugador
ID es el identificador único del jugador.
El hoyo y el día son obvios y la puntuación es la puntuación del jugador en ese hoyo.
En la cláusula FROM de la sentencia SELECT, pl es la lista completa de jugadores extraída del documento para el primer día, primer hoyo (hoyos:1:1).
Rs es el resultado de los jugadores, golpe a golpe, hoyo a hoyo. En primer lugar, anulamos ese array un par de veces para proyectar los detalles de cada hoyo y la puntuación de ese hoyo, determinada por array_length(hps.Sks).
Una vez que tenemos la puntuación hoyo por hoyo, es fácil escribir la consulta final para agregar por jugador y por día.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
seleccione d.nombre, suma(caso cuando d.día = 1 entonces d.puntuación si no 0 fin) R1, suma(caso cuando d.día = 2 entonces d.puntuación si no 0 fin) R2, suma(caso cuando d.día = 3 entonces d.puntuación si no 0 fin) R3, suma(caso cuando d.día = 4 entonces d.puntuación si no 0 fin) R4, suma(d.puntuación) T de d grupo por d.nombre pedir por d.nombre |
**La cláusula WITH es la característica de expresión de tabla común (CTE) en la próxima versión de Mad-Hatter. La forma antigua de hacer esto en Couchbase 5.5 o inferior es usando la cláusula LET. Publica la pregunta en el foro de Couchbase si necesitas ayuda aquí).
Tarea 2: Ahora, crea la tabla de clasificación completa y añade el botón CORTE información. Los golfistas eliminados no jugarán la tercera ni la cuarta ronda. Usamos esta información para determinar los jugadores que han sido eliminados.
Consulta 2. Toma la consulta anterior y nómbrala como una tabla común dx y luego añade la siguiente expresión para determinar ese corte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
( CASO CUANDO ( d2.R1 = 0 O d2.R2 = 0 O d2.R3 = 0 O d2.R4 = 0 ) ENTONCES "CORTE" ELSE FALTA FIN ) AS CORTE |
Aquí está la consulta completa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
CON dy AS ( SELECCIONE pl.hnum AS holedn, pl.ps.Nat AS país,(pl.ps.FN || " " || pl.ps.LN) AS nombre, pl.ps.ID AS ID, longitud_array(hps.Sks) AS puntuación, hpl.agujero AS `agujero`, hpl.día AS `día` DESDE ( SELECCIONE meta(usopen).id AS hnum, ps DESDE usopen UTILICE llaves "agujeros:1:1" sin anestesia Ps AS ps ) pl INTERIOR ÚNASE A ( SELECCIONE TONÚMERO(dividir(meta(usopen).id, ":") [1]) AS `agujero`, TONÚMERO(dividir(meta(usopen).id, ":") [2]) AS `día`, hps DESDE usopen sin anestesia Rs AS rs sin anestesia rs.Hs AS hs sin anestesia hs.HPs AS hps ) hpl EN (pl.ps.ID = hps.ID) ), dx AS ( SELECCIONE d.nombre, suma( CASO CUANDO d.día = 1 ENTONCES d.puntuación ELSE 0 FIN ) R1, suma( CASO CUANDO d.día = 2 ENTONCES d.puntuación ELSE 0 FIN ) R2, suma( CASO CUANDO d.día = 3 ENTONCES d.puntuación ELSE 0 FIN ) R3, suma( CASO CUANDO d.día = 4 ENTONCES d.puntuación ELSE 0 FIN ) R4, suma(d.puntuación) T DESDE dy AS d GRUPO POR d.nombre PEDIR POR d.nombre ) SELECCIONE d2.nombre, d2.R1, d2.R2, d2.R3, d2.R4, d2.T,( CASO CUANDO ( d2.R1 = 0 O d2.R2 = 0 O d2.R3 = 0 O d2.R4 = 0 ) ENTONCES "CORTAR" ELSE FALTA FIN ) AS CORTE DESDE dx AS d2 PEDIR POR CORTE ASC, d2.T ASC |
Tarea 3: Determinar los ganadores.
Tenemos que clasificar a los jugadores en función de la puntuación total para determinar quién ha ganado el torneo. Las clasificaciones se omiten si hay empates en las puntuaciones. Cómo hacerlo en SQL sin funciones de ventana es caro. Aquí, escribimos la consulta utilizando la función de ventana RANK(). Las funciones de ventana son una característica en N1QL en la próxima versión (Mad-Hatter)
Consulta 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
CON dy AS ( SELECCIONE pl.hnum AS holedn ,pl.ps.Nat AS país ,(pl.ps.FN || " " || pl.ps.LN) AS nombre ,pl.ps.ID AS ID ,longitud_array(hps.Sks) AS puntuación ,hpl.agujero AS `agujero` ,hpl.día AS `día` DESDE ( SELECCIONE meta(usopen).id AS hnum ,ps DESDE usopen UTILICE llaves "agujeros:1:1" sin anestesia Ps AS ps ) pl INTERIOR ÚNASE A ( SELECCIONE TONÚMERO(dividir(meta(usopen).id, ":") [1]) AS `agujero` ,TONÚMERO(dividir(meta(usopen).id, ":") [2]) AS `día` ,hps DESDE usopen sin anestesia Rs AS rs sin anestesia rs.Hs AS hs sin anestesia hs.HPs AS hps ) hpl EN (pl.ps.ID = hps.ID) ) ,dx AS ( SELECCIONE d.nombre ,suma(CASO CUANDO d.día = 1 ENTONCES d.puntuación ELSE 0 FIN) R1 ,suma(CASO CUANDO d.día = 2 ENTONCES d.puntuación ELSE 0 FIN) R2 ,suma(CASO CUANDO d.día = 3 ENTONCES d.puntuación ELSE 0 FIN) R3 ,suma(CASO CUANDO d.día = 4 ENTONCES d.puntuación ELSE 0 FIN) R4 ,suma(d.puntuación) T DESDE dy AS d GRUPO POR d.nombre PEDIR POR d.nombre ) SELECCIONE d2.nombre ,d2.R1 ,d2.R2 ,d2.R3 ,d2.R4 ,d2.T ,RANK() EN (PEDIR POR d2.T + CORTE) AS Rango DESDE dx AS d2 LET CORTE = ( CASO CUANDO ( d2.R1 = 0 O d2.R2 = 0 O d2.R3 = 0 O d2.R4 = 0 ) ENTONCES 1000 ELSE 0 FIN ) PEDIR POR Rango |
Fíjese en que faltan los puestos 4, 8, 9, 10 y 11 debido a los empates.
Tarea 4: Ahora, vamos a averiguar cómo le fue a cada jugador después de la ronda1, ronda2, ronda3 en comparación con la ronda final. Utilizando las funciones de ventana, resulta tan fácil como hacer desaparecer los malvaviscos cubiertos de chocolate.
Consulta 4: Utilice la misma función RANK(), ordenando por la puntuación de cada día (día1, día1+día2, día1+día2+día3) en lugar de sólo por la puntuación final.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
CON dy AS ( SELECCIONE pl.hnum AS holedn, pl.ps.Nat AS país,(pl.ps.FN || " " || pl.ps.LN) AS nombre, pl.ps.ID AS ID, longitud_array(hps.Sks) AS puntuación, hpl.agujero AS `agujero`, hpl.día AS `día` DESDE ( SELECCIONE meta(usopen).id AS hnum, ps DESDE usopen UTILICE llaves "agujeros:1:1" sin anestesia Ps AS ps ) pl INTERIOR ÚNASE A ( SELECCIONE TONÚMERO(dividir(meta(usopen).id, ":") [1]) AS `agujero`, TONÚMERO(dividir(meta(usopen).id, ":") [2]) AS `día`, hps DESDE usopen sin anestesia Rs AS rs sin anestesia rs.Hs AS hs sin anestesia hs.HPs AS hps ) hpl EN (pl.ps.ID = hps.ID) ), dx AS ( SELECCIONE d.nombre, suma( CASO CUANDO d.día = 1 ENTONCES d.puntuación ELSE 0 FIN ) R1, suma( CASO CUANDO d.día = 2 ENTONCES d.puntuación ELSE 0 FIN ) R2, suma( CASO CUANDO d.día = 3 ENTONCES d.puntuación ELSE 0 FIN ) R3, suma( CASO CUANDO d.día = 4 ENTONCES d.puntuación ELSE 0 FIN ) R4, suma(d.puntuación) T DESDE dy AS d GRUPO POR d.nombre PEDIR POR d.nombre ) SELECCIONE d2.nombre, d2.R1, d2.R2, d2.R3, d2.R4, d2.T, DENSE_RANK() EN ( PEDIR POR d2.T + CORTE ) AS rankMoney, RANK() EN ( PEDIR POR d2.T + CORTE ) AS rankFinal, RANK() EN ( PEDIR POR d2.R1 ) AS round1rank, RANK() EN ( PEDIR POR d2.R1 + d2.R2 ) AS round2rank, RANK() EN ( PEDIR POR d2.R1 + d2.R2 + d2.R3 + CORTE ) AS round3rank DESDE dx AS d2 LET CORTE = ( CASO CUANDO ( d2.R1 = 0 O d2.R2 = 0 O d2.R3 = 0 O d2.R4 = 0 ) ENTONCES 1000 ELSE 0 FIN ) PEDIR POR rankFinal, round1rank, round2rank, round3rank |
Ahora puedes ver cómo subieron o bajaron los jugadores cada día.
Tarea 5: Crear el cuadro de mando completo para el líder utilizando las estadísticas básicas tiro a tiro.
Consulta 5: Brooks Koepka es el ganador final del US open. Obtengamos sus puntuaciones, hoyo por hoyo y obtengamos las puntuaciones acumuladas para él por ronda. Observa cómo la simple SUM() y el agregado COUNT() funcionan como una función ventana con la cláusula OVER().
1 |
SUM(d2.puntuación) EN (PARTICIÓN POR d2.día PEDIR POR d2.agujero) hst |
Primero se divide la puntuación por día y luego por hoyo - especificado por la cláusula PARTITION BY, en el orden de los hoyos 1-18. A continuación, la SUMA suma las puntuaciones hasta el momento.
1 |
SUM(d3.puntuación) EN (PEDIR POR d3.día,d3.agujero) ToTScore |
Esta función SUM() simplemente suma la puntuación desde el día 1, hoyo 1 hasta el día 4, hoyo 18 - esto es especificado por el ORDER BY d3.day, d3.hole dentro de la cláusula OVER().. El campo ToTScore muestra los cortos totales del torneo por Koepka en cada hoyo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
CON dy AS ( SELECCIONE pl.hnum AS holedn, pl.ps.Nat AS país,(pl.ps.FN || " " || pl.ps.LN) AS nombre, pl.ps.ID AS ID, longitud_array(hps.Sks) AS puntuación, hpl.agujero AS `agujero`, hpl.día AS `día`, hpl.Par AS Par DESDE ( SELECCIONE meta(usopen).id AS hnum, ps DESDE usopen UTILICE llaves "agujeros:1:1" sin anestesia Ps AS ps DONDE ps.LN = "Koepka" ) pl INTERIOR ÚNASE A ( SELECCIONE TONÚMERO(dividir(meta(usopen).id, ":") [1]) AS `agujero`, TONÚMERO(dividir(meta(usopen).id, ":") [2]) AS `día`, hs.Par, hps DESDE usopen sin anestesia Rs AS rs sin anestesia rs.Hs AS hs sin anestesia hs.HPs AS hps ) hpl EN (pl.ps.ID = hps.ID) ), dx AS ( SELECCIONE d.nombre, d.día, d.puntuación, d.agujero, d.Par DESDE dy AS d PEDIR POR d.nombre ), dz AS ( SELECCIONE d2.día, d2.agujero, d2.puntuación, SUM(d2.puntuación) EN ( PARTICIÓN POR d2.día PEDIR POR d2.agujero ) hst, d2.Par, SUM(d2.Par) EN ( PARTICIÓN POR d2.día PEDIR POR d2.agujero ) hpr DESDE dx AS d2 LET CORTE = ( CASO CUANDO ( d2.R1 = 0 O d2.R2 = 0 O d2.R3 = 0 O d2.R4 = 0 ) ENTONCES 1000 ELSE 0 FIN ) PEDIR POR d2.día, d2.agujero ) SELECCIONE d3.Par, d3.día, d3.agujero, d3.hst, d3.puntuación,(d3.hst - d3.hpr) ToPar, suma(d3.puntuación) EN ( PEDIR POR d3.día, d3.agujero ) ToTScore, cuente(1) EN ( PEDIR POR d3.día, d3.agujero ) HoleNum DESDE dz AS d3 |