공동 저자: Sitaram Vemulapalli, 수석 엔지니어, 카우치베이스 R&D.
"내 친구는 JSON에 답을 숨기고 있다" - Bob Dylan
많은 public JSON 데이터 세트와 awesome JSON 데이터 세트. 귀사를 포함한 모든 기업은 설문조사, 캠페인, 포럼의 결과 등 많은 데이터를 JSON으로 저장하고 있습니다.
JSON에 스킨을 입히는 방법에는 여러 가지가 있습니다. 원하는 모든 보고서, 시각화에 대해 Python 프로그램을 작성할 수 있습니다. 또는 다음을 사용할 수 있습니다. N1QL (JSON용 SQL)을 적합한 알고리즘 생성 를 사용하여 JSON 데이터를 분석할 수 있습니다. 이 문서에서는 N1QL을 사용하여 인사이트를 빠르게 추출하는 방법을 보여드립니다. 또한 다음 릴리스에 추가될 두 가지 기능도 사용합니다: 공통 테이블 표현식(CTE)과 창 함수입니다.
목표: US 오픈 골프 점수에 대한 공개 JSON 데이터 세트를 사용하여 간단한 리더보드, 순위 등을 생성합니다.
그 일환으로 세 가지 일을 하게 됩니다:
- Couchbase에 데이터를 쉽게 수집하세요.
- 이 JSON 데이터의 값을 즉시 가져오기 시작하세요.
- 새로운 기능을 사용하여 유용한 보고서를 빠르게 생성할 수 있도록 JSON을 구체화하세요.
소스 데이터: https://github.com/jackschultz/usopen
이 게시물의 쿼리는 다음에서도 확인할 수 있습니다: https://github.com/keshavmr/usopen-golf-queries
데이터 저장소 구조: 이 GitHub 리포지토리 https://github.com/jackschultz/usopen US 오픈 골프 -2018 데이터가 포함되어 있습니다. 각 홀에 대해 날짜별로 별도의 문서로 제공됩니다.
각 문서는 이러한 구조를 가집니다. 이것은 첫째 날 1번 홀에 대한 문서입니다. 제출된 Ps에는 각각 고유 ID를 가진 플레이어 목록이 있습니다.
각 플레이어의 플레이 통계는 스트로크별로 그 뒤를 따릅니다. 플레이어는 해당 플레이어의 필드 고유 ID를 사용하여 스코어와 매칭됩니다.
인사이트를 얻기 시작하세요:
쿼리를 시작하기 전에 버킷에 기본 인덱스를 만드세요.
usopen에 기본 인덱스를 만듭니다;
작업 1: 라운드별 플레이어 점수와 최종 총점에 대한 보고서를 작성합니다.
상향식으로 JSON을 가지고 놀다가 이 쿼리를 생각해냈습니다. 설명은 쿼리 뒤에 있습니다.
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 |
WITH d AS ( 선택 pl.hnum AS holedn, pl.ps.Nat AS 국가, (pl.ps.FN || " " || pl.ps.LN) AS 이름, pl.ps.ID AS ID, 배열_길이(hps.Sks) AS 점수, hpl.구멍 AS `구멍`, hpl.일 AS `일` FROM ( 선택 메타(usopen).id AS hnum, ps FROM usopen 사용 키 "구멍:1:1" unnest 추신 AS ps ) pl INNER JOIN ( 선택 숫자(분할(메타(usopen).id, ":") [1]) AS `구멍`, 숫자(분할(메타(usopen).id, ":") [2]) AS `일`, hps FROM usopen unnest Rs AS rs UNNEST rs.Hs AS hs UNNEST hs.HP AS hps ) hpl 켜기 (pl.ps.ID = hps.ID) ) 선택 d.이름, SUM( 사례 언제 d.일 = 1 그때 d.점수 기타 0 END ) R1, SUM( 사례 언제 d.일 = 2 그때 d.점수 기타 0 END ) R2, SUM( 사례 언제 d.일 = 3 그때 d.점수 기타 0 END ) R3, SUM( 사례 언제 d.일 = 4 그때 d.점수 기타 0 END ) R4, SUM(d.점수) T FROM d 그룹 BY d.이름 주문 BY d.이름 |
표 형식의 결과(카우치베이스 쿼리 워크벤치에서 표 형식)
쿼리를 블록별로 살펴보겠습니다.
WITH d 절을 보세요. 이 문은 일별-홀별-샷별 데이터에서 간단한 스칼라 값으로 JSON을 풀어줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "d": { "ID": "37189", "country": "USA", "day": 1, "구멍": 10, "holedn": "구멍:1:1", "name": "해롤드 바너", "score": 6 } } |
Holedn은 문서 키 - 홀-일-번호입니다.
국가는 플레이어의 국적입니다.
ID는 플레이어의 고유 ID입니다.
홀과 날짜는 분명하며 점수는 해당 홀에 대한 플레이어의 점수입니다.
SELECT 문의 FROM 절에서 pl은 문서에서 가져온 첫날 첫 번째 홀(홀:1:1)의 전체 선수 목록입니다.
Rs는 플레이어의 샷별, 홀별 결과입니다. 먼저 배열의 중첩을 몇 번 풀어 각 홀의 세부 정보와 해당 홀의 점수를 array_length(hps.Sks)에 따라 투영합니다.
홀별 스코어가 나오면 플레이어별, 요일별로 집계할 최종 쿼리를 쉽게 작성할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
선택 d.이름, 합계(case 언제 d.일 = 1 다음 d.점수 else 0 끝) R1, 합계(case 언제 d.일 = 2 다음 d.점수 else 0 끝) R2, 합계(case 언제 d.일 = 3 다음 d.점수 else 0 끝) R3, 합계(case 언제 d.일 = 4 다음 d.점수 else 0 끝) R4, 합계(d.점수) T 에서 d 그룹 by d.이름 주문 by d.이름 |
**WITH 절은 곧 출시될 Mad-Hatter 릴리즈의 공통 테이블 표현식(CTE) 기능입니다. Couchbase 5.5 이하에서 이 작업을 수행하는 기존 방법은 LET 절을 사용하는 것입니다. 도움이 필요하면 Couchbase 포럼에 질문을 게시하세요(여기를 참조하세요).
작업 2: 이제 전체 리더보드를 생성하고 CUT 정보. 컷을 받은 골퍼는 3라운드 또는 4라운드에 출전하지 않습니다. 이 정보를 사용하여 컷 탈락한 선수를 결정합니다.
쿼리 2. 이전 쿼리를 가져와서 공통 테이블 dx로 이름을 지정한 다음 다음 표현식을 추가하여 해당 컷을 결정합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
( 사례 언제 ( d2.R1 = 0 또는 d2.R2 = 0 또는 d2.R3 = 0 또는 d2.R4 = 0 ) 그때 "CUT" 기타 누락 END ) AS CUT |
전체 쿼리는 다음과 같습니다:
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 |
WITH dy AS ( 선택 pl.hnum AS holedn, pl.ps.Nat AS 국가,(pl.ps.FN || " " || pl.ps.LN) AS 이름, pl.ps.ID AS ID, 배열_길이(hps.Sks) AS 점수, hpl.구멍 AS `구멍`, hpl.일 AS `일` FROM ( 선택 메타(usopen).id AS hnum, ps FROM usopen 사용 키 "구멍:1:1" unnest 추신 AS ps ) pl INNER JOIN ( 선택 숫자(분할(메타(usopen).id, ":") [1]) AS `구멍`, 숫자(분할(메타(usopen).id, ":") [2]) AS `일`, hps FROM usopen unnest Rs AS rs unnest rs.Hs AS hs unnest hs.HP AS hps ) hpl 켜기 (pl.ps.ID = hps.ID) ), dx AS ( 선택 d.이름, 합계( 사례 언제 d.일 = 1 그때 d.점수 기타 0 END ) R1, 합계( 사례 언제 d.일 = 2 그때 d.점수 기타 0 END ) R2, 합계( 사례 언제 d.일 = 3 그때 d.점수 기타 0 END ) R3, 합계( 사례 언제 d.일 = 4 그때 d.점수 기타 0 END ) R4, 합계(d.점수) T FROM dy AS d 그룹 BY d.이름 주문 BY d.이름 ) 선택 d2.이름, d2.R1, d2.R2, d2.R3, d2.R4, d2.T,( 사례 언제 ( d2.R1 = 0 또는 d2.R2 = 0 또는 d2.R3 = 0 또는 d2.R4 = 0 ) 그때 "CUT" 기타 누락 END ) AS CUT FROM dx AS d2 주문 BY CUT ASC, d2.T ASC |
과제 3: 우승자를 결정합니다.
토너먼트 우승자를 결정하기 위해 총점을 기준으로 플레이어의 순위를 매겨야 합니다. 점수가 동점인 경우 순위는 건너뜁니다. SQL에서 이 작업 수행하기 창 기능이 없으면 비용이 많이 듭니다.. 여기서는 RANK() 창 함수를 사용하여 쿼리를 작성합니다. 창 함수는 곧 출시될 N1QL(Mad-Hatter)의 기능입니다.
쿼리 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 |
WITH dy AS ( 선택 pl.hnum AS holedn ,pl.ps.Nat AS 국가 ,(pl.ps.FN || " " || pl.ps.LN) AS 이름 ,pl.ps.ID AS ID ,배열_길이(hps.Sks) AS 점수 ,hpl.구멍 AS `구멍` ,hpl.일 AS `일` FROM ( 선택 메타(usopen).id AS hnum ,ps FROM usopen 사용 키 "구멍:1:1" unnest 추신 AS ps ) pl INNER JOIN ( 선택 숫자(분할(메타(usopen).id, ":") [1]) AS `구멍` ,숫자(분할(메타(usopen).id, ":") [2]) AS `일` ,hps FROM usopen unnest Rs AS rs unnest rs.Hs AS hs unnest hs.HP AS hps ) hpl 켜기 (pl.ps.ID = hps.ID) ) ,dx AS ( 선택 d.이름 ,합계(사례 언제 d.일 = 1 그때 d.점수 기타 0 END) R1 ,합계(사례 언제 d.일 = 2 그때 d.점수 기타 0 END) R2 ,합계(사례 언제 d.일 = 3 그때 d.점수 기타 0 END) R3 ,합계(사례 언제 d.일 = 4 그때 d.점수 기타 0 END) R4 ,합계(d.점수) T FROM dy AS d 그룹 BY d.이름 주문 BY d.이름 ) 선택 d2.이름 ,d2.R1 ,d2.R2 ,d2.R3 ,d2.R4 ,d2.T ,RANK() OVER (주문 BY d2.T + CUT) AS 순위 FROM dx AS d2 LET CUT = ( 사례 언제 ( d2.R1 = 0 또는 d2.R2 = 0 또는 d2.R3 = 0 또는 d2.R4 = 0 ) 그때 1000 기타 0 END ) 주문 BY 순위 |
동점 점수로 인해 4, 8, 9, 10, 11 순위가 누락된 것을 주목하세요!
작업 4: 이제 각 플레이어가 1라운드, 2라운드, 3라운드를 거쳐 최종 라운드에서 어떤 성적을 거뒀는지 알아봅시다. 창 기능을 사용하면 초콜릿으로 덮인 마시멜로를 사라지게 하는 것이 쉬워집니다.
쿼리 4: 최종 점수 대신 각 날짜의 점수(day1, day1+day2, day1+day2+day3)를 기준으로 ORDER BY로 동일한 RANK() 함수를 사용합니다.
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 |
WITH dy AS ( 선택 pl.hnum AS holedn, pl.ps.Nat AS 국가,(pl.ps.FN || " " || pl.ps.LN) AS 이름, pl.ps.ID AS ID, 배열_길이(hps.Sks) AS 점수, hpl.구멍 AS `구멍`, hpl.일 AS `일` FROM ( 선택 메타(usopen).id AS hnum, ps FROM usopen 사용 키 "구멍:1:1" unnest 추신 AS ps ) pl INNER JOIN ( 선택 숫자(분할(메타(usopen).id, ":") [1]) AS `구멍`, 숫자(분할(메타(usopen).id, ":") [2]) AS `일`, hps FROM usopen unnest Rs AS rs unnest rs.Hs AS hs unnest hs.HP AS hps ) hpl 켜기 (pl.ps.ID = hps.ID) ), dx AS ( 선택 d.이름, 합계( 사례 언제 d.일 = 1 그때 d.점수 기타 0 END ) R1, 합계( 사례 언제 d.일 = 2 그때 d.점수 기타 0 END ) R2, 합계( 사례 언제 d.일 = 3 그때 d.점수 기타 0 END ) R3, 합계( 사례 언제 d.일 = 4 그때 d.점수 기타 0 END ) R4, 합계(d.점수) T FROM dy AS d 그룹 BY d.이름 주문 BY d.이름 ) 선택 d2.이름, d2.R1, d2.R2, d2.R3, d2.R4, d2.T, DENSE_RANK() OVER ( 주문 BY d2.T + CUT ) AS rankMoney, RANK() OVER ( 주문 BY d2.T + CUT ) AS rankFinal, RANK() OVER ( 주문 BY d2.R1 ) AS 라운드1랭크, RANK() OVER ( 주문 BY d2.R1 + d2.R2 ) AS 라운드2랭크, RANK() OVER ( 주문 BY d2.R1 + d2.R2 + d2.R3 + CUT ) AS 라운드3랭크 FROM dx AS d2 LET CUT = ( 사례 언제 ( d2.R1 = 0 또는 d2.R2 = 0 또는 d2.R3 = 0 또는 d2.R4 = 0 ) 그때 1000 기타 0 END ) 주문 BY rankFinal, 라운드1랭크, 라운드2랭크, 라운드3랭크 |
이제 플레이어가 매일 어떻게 움직였는지 확인할 수 있습니다.
작업 5: 기본 샷별 통계를 사용하여 리더에 대한 전체 스코어카드를 작성합니다.
쿼리 5: 브룩스 켑카가 US 오픈의 최종 우승자입니다. 그의 점수를 홀별로 가져와서 라운드별 누적 점수를 구해 보겠습니다. 간단한 합계() 및 카운트() 집계가 OVER() 절과 함께 창 함수로 어떻게 작동하는지 주목하세요.
1 |
SUM(d2.점수) OVER (파티션 BY d2.일 주문 BY d2.구멍) hst |
먼저 점수를 날짜별로 분할한 다음, 1-18번 홀의 순서대로 PARTITION BY 절에 지정된 홀별로 분할합니다. 그런 다음 SUM은 지금까지의 점수를 합산합니다.
1 |
SUM(d3.점수) OVER (주문 BY d3.일,d3.구멍) ToTScore |
이 SUM() 함수는 단순히 첫째 날 1번 홀부터 넷째 날 18번 홀까지의 점수를 합산하며, 이는 OVER() 절 내의 ORDER BY d3.day, d3.hole로 지정됩니다. ToTScore 필드에는 각 홀에서 켑카의 토너먼트 총 타수가 표시됩니다.
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 |
WITH dy AS ( 선택 pl.hnum AS holedn, pl.ps.Nat AS 국가,(pl.ps.FN || " " || pl.ps.LN) AS 이름, pl.ps.ID AS ID, 배열_길이(hps.Sks) AS 점수, hpl.구멍 AS `구멍`, hpl.일 AS `일`, hpl.Par AS Par FROM ( 선택 메타(usopen).id AS hnum, ps FROM usopen 사용 키 "구멍:1:1" unnest 추신 AS ps 어디 ps.LN = "Koepka" ) pl INNER JOIN ( 선택 숫자(분할(메타(usopen).id, ":") [1]) AS `구멍`, 숫자(분할(메타(usopen).id, ":") [2]) AS `일`, hs.Par, hps FROM usopen unnest Rs AS rs unnest rs.Hs AS hs unnest hs.HP AS hps ) hpl 켜기 (pl.ps.ID = hps.ID) ), dx AS ( 선택 d.이름, d.일, d.점수, d.구멍, d.Par FROM dy AS d 주문 BY d.이름 ), dz AS ( 선택 d2.일, d2.구멍, d2.점수, SUM(d2.점수) OVER ( 파티션 BY d2.일 주문 BY d2.구멍 ) hst, d2.Par, SUM(d2.Par) OVER ( 파티션 BY d2.일 주문 BY d2.구멍 ) hpr FROM dx AS d2 LET CUT = ( 사례 언제 ( d2.R1 = 0 또는 d2.R2 = 0 또는 d2.R3 = 0 또는 d2.R4 = 0 ) 그때 1000 기타 0 END ) 주문 BY d2.일, d2.구멍 ) 선택 d3.Par, d3.일, d3.구멍, d3.hst, d3.점수,(d3.hst - d3.hpr) ToPar, 합계(d3.점수) OVER ( 주문 BY d3.일, d3.구멍 ) ToTScore, 카운트(1) OVER ( 주문 BY d3.일, d3.구멍 ) HoleNum FROM dz AS d3 |