Trabaja con mediciones
En este documento, se muestra cómo definir y consultar mediciones en tus grafos. Puedes usar medidas para asegurarte de que las agregaciones se realicen correctamente en las uniones.
Descripción general
Una medida es una propiedad agregada definida dentro de la cláusula PROPERTIES
de una tabla de nodos o bordes. Las mediciones se definen con una expresión de medición, que
se crea con la palabra clave MEASURE alrededor de una de las siguientes opciones:
- una expresión de agregación
- una expresión que combina funciones de agregación con operaciones escalares
- una medición derivada, que es una combinación de mediciones unidas en la función
AGGque se definen en la misma tabla de elementos
Las mediciones admiten las siguientes funciones de agregación:
SUMAVGCOUNTMINMAXAGG(para mediciones derivadas)
Todas las funciones de agregación, excepto AGG, admiten la palabra clave DISTINCT. Para obtener más
información sobre la expresión de medición admitida, consulta el
tipo de medición.
Las mediciones definen su agregación en referencia a la KEY de la tabla de nodos o
aristas en la que se definen. Esto significa que, cuando consultas una medición,
la agregación se realiza correctamente, incluso si la tabla subyacente se une de
una manera que hace que se dupliquen las filas.
No puedes hacer referencia a una propiedad definida por una medición en una consulta de GQL. En su lugar,
accedes a las mediciones llamando a la
TVF GRAPH_EXPAND
para consultar una representación relacional de tu grafo. De forma predeterminada, GRAPH_EXPAND
devuelve una representación de tabla plana de tu grafo con una columna por
propiedad. También puedes especificar flatten_results => FALSE para devolver una
tabla jerárquica con columnas STRUCT anidadas. Si tu grafo contiene varias
tablas de nodos raíz, puedes especificar el argumento start_node para elegir qué
tabla raíz recorrer.
Esta función no acepta todos los tipos de grafos. Para obtener más información sobre
qué grafos forman una entrada válida, consulta las limitaciones de entrada.
Para ejecutar consultas de GQL, debes tener una
reserva que use la
edición Enterprise o Enterprise Plus.
Si usas precios a pedido,
puedes llamar a la función GRAPH_EXPAND para ejecutar consultas en SQL en tu grafo.
Puedes llamar a la
función AGG
en el resultado de la TVF GRAPH_EXPAND para agregar
las propiedades según la agregación definida en
la medición.
Define mediciones
Las mediciones se definen dentro de la
cláusula PROPERTIES
de una definición de tabla de nodos o aristas con la palabra clave MEASURE() alrededor de una
expresión de medición.
En el siguiente ejemplo, se crea un conjunto de datos llamado university y tablas
para universidades, departamentos y cursos:
CREATE SCHEMA IF NOT EXISTS university;
CREATE OR REPLACE TABLE university.College (
college_id INT64 PRIMARY KEY NOT ENFORCED,
college_name STRING
);
CREATE OR REPLACE TABLE university.Department (
dept_id INT64 PRIMARY KEY NOT ENFORCED,
dept_name STRING,
college_id INT64,
budget FLOAT64,
FOREIGN KEY (college_id) REFERENCES university.College(college_id) NOT ENFORCED
);
CREATE OR REPLACE TABLE university.Course (
course_id INT64 PRIMARY KEY NOT ENFORCED,
course_name STRING,
dept_id INT64,
credits INT64,
FOREIGN KEY (dept_id) REFERENCES university.Department(dept_id) NOT ENFORCED
);
INSERT INTO university.College (college_id, college_name)
VALUES (101, 'College of Engineering'),
(102, 'College of Arts');
INSERT INTO university.Department (dept_id, dept_name, college_id, budget)
VALUES (1001, 'Computer Science', 101, 500000),
(1002, 'Mechanical Engineering', 101, 400000),
(1003, 'Fine Arts', 102, 200000),
(1004, 'Research', 101, 50000);
INSERT INTO university.Course (course_id, course_name, dept_id, credits)
VALUES (1, 'Intro to CS', 1001, 3),
(2, 'Data Structures', 1001, 4),
(3, 'Thermodynamics', 1002, 3),
(4, 'Oil Painting', 1003, 2);
La siguiente sentencia crea un grafo llamado SchoolGraph que define
medidas en las propiedades de los nodos Department y Course.
Debes proporcionar un alias para las propiedades definidas por una medida.
CREATE OR REPLACE PROPERTY GRAPH university.SchoolGraph
NODE TABLES (
university.College
KEY(college_id)
PROPERTIES(college_id, college_name),
university.Department
KEY(dept_id)
PROPERTIES(dept_id, dept_name, college_id,
budget OPTIONS(description="Department budget in USD"),
MEASURE(SUM(budget)) AS total_budget,
MEASURE(SUM(budget) * 1.10) AS budget_with_overhead OPTIONS(
description="Total department budget including 10% overhead",
extensions=[("unit", "USD"), ("tier", "planning")]
)),
university.Course
KEY(course_id)
PROPERTIES(
course_id,
course_name,
credits,
dept_id,
MEASURE(AVG(credits)) AS avg_credits,
MEASURE(SUM(credits)) AS total_credits,
MEASURE(COUNT(course_id)) AS course_count,
MEASURE(COUNT(DISTINCT CASE WHEN credits >= 3 THEN course_id ELSE NULL END)) AS major_course_count,
MEASURE(SAFE_DIVIDE(AGG(total_credits), NULLIF(AGG(course_count), 0))) AS computed_avg_credits)
)
EDGE TABLES (
university.Department AS CollegeDept
SOURCE KEY (college_id) REFERENCES College (college_id)
DESTINATION KEY (dept_id) REFERENCES Department (dept_id),
university.Course AS DeptCourse
SOURCE KEY (dept_id) REFERENCES Department (dept_id)
DESTINATION KEY (course_id) REFERENCES Course (course_id)
);

La medición total_budget se define como MEASURE(SUM(budget)). La medición
bloquea la agregación en KEY, que es dept_id.
Mediciones derivadas
Una medida derivada es una medida que hace referencia a otras medidas definidas
en la misma tabla de elementos con la función AGG(). Esta funcionalidad
te permite crear jerarquías de mediciones reutilizables.
Por ejemplo, en university.Course, la medición computed_avg_credits calcula la proporción entre las mediciones total_credits y course_count:
MEASURE(SAFE_DIVIDE(AGG(total_credits), NULLIF(AGG(course_count), 0))) AS computed_avg_credits
Las mediciones derivadas conservan la propiedad de bloqueo de nivel de detalle de la clave de la tabla de elementos subyacente. Las dependencias de las mediciones se resuelven en el tiempo de compilación del grafo. Las referencias circulares entre mediciones devuelven un error.
Información sobre el recuento excesivo
Cuando unes tablas, los datos se repiten cada vez que hay una relación de uno a varios en tus datos. Por ejemplo, si unes las tablas Course, Department
y College, un departamento con varios cursos aparece en varias
filas en el resultado:
SELECT
college_name,
dept_name,
course_name,
budget
FROM university.Course
LEFT JOIN university.Department
ON Course.dept_id = Department.dept_id
LEFT JOIN university.College
ON Department.college_id = College.college_id;
/*------------------------+------------------------+-----------------+----------+
| college_name | dept_name | course_name | budget |
+------------------------+------------------------+-----------------+----------+
| College of Engineering | Computer Science | Intro to CS | 500000.0 |
| College of Engineering | Computer Science | Data Structures | 500000.0 |
| College of Engineering | Mechanical Engineering | Thermodynamics | 400000.0 |
| College of Arts | Fine Arts | Oil Painting | 200000.0 |
+------------------------+------------------------+-----------------+----------*/
Si intentas calcular el presupuesto total por facultad con SUM(budget),
el presupuesto del departamento de Ciencias de la Computación se cuenta dos veces. Puedes evitar este
problema consultando la tabla Department directamente, pero este enfoque no
funciona si quieres calcular varias agregaciones de diferentes tablas que
contribuyen a tus datos unidos. En la siguiente sección, se muestra cómo las medidas resuelven
este problema.
Consulta un grafo con medidas
Puedes consultar un grafo con mediciones usando GQL, pero tu consulta no puede hacer referencia directamente a ninguna propiedad definida por una medición. Por ejemplo, la siguiente consulta hace referencia a nodos que tienen propiedades de medición definidas, pero no usa ni devuelve ninguno de los campos de medición:
GRAPH university.SchoolGraph
MATCH (c:College)->(d:Department)->(course:Course)
RETURN c.college_name, d.dept_name, course.course_name;
/*------------------------+------------------------+-----------------+
| college_name | dept_name | course_name |
+------------------------+------------------------+-----------------+
| College of Engineering | Computer Science | Intro to CS |
| College of Engineering | Computer Science | Data Structures |
| College of Engineering | Mechanical Engineering | Thermodynamics |
| College of Arts | Fine Arts | Oil Painting |
+------------------------+------------------------+-----------------*/
Para trabajar con mediciones, usa la función con valor de tabla (TVF) GRAPH_EXPAND para consultar tu grafo como una tabla relacional:
GRAPH_EXPAND(
graph_name [, start_node => 'NodeName'] [, flatten_results => TRUE|FALSE]
)
La función acepta los siguientes argumentos:
graph_name: Es unSTRINGque contiene el nombre del gráfico.start_node: Es unSTRINGopcional que especifica la tabla de nodos de inicio para el recorrido. Este argumento es obligatorio si el grafo tiene varias tablas de nodos raíz.flatten_results: Es unBOOLopcional (el valor predeterminado esTRUE):TRUE: Aplana todas las propiedades de nodos y bordes en columnas de nivel superior con prefijos<element>_<property>.FALSE: Devuelve las propiedades del nodo raíz como columnas de nivel superior sin prefijo y las tablas vecinas unidas como columnasSTRUCT.
Usa el resultado sin compactar
Cuando flatten_results se establece en TRUE (el modo predeterminado), las columnas de la
tabla de salida se nombran concatenando la etiqueta de la tabla de nodos o
aristas y el nombre de la propiedad, por ejemplo, Course_course_name o
Department_total_budget. En la siguiente consulta, se muestra un resultado de ejemplo de la
TVF GRAPH_EXPAND:
SELECT
College_college_name,
Department_dept_name,
Department_budget,
Course_course_name
FROM
GRAPH_EXPAND("university.SchoolGraph");
/*------------------------+------------------------+-------------------+--------------------+
| College_college_name | Department_dept_name | Department_budget | Course_course_name |
+------------------------+------------------------+-------------------+--------------------+
| College of Engineering | Computer Science | 500000.0 | Intro to CS |
| College of Engineering | Computer Science | 500000.0 | Data Structures |
| College of Engineering | Mechanical Engineering | 400000.0 | Thermodynamics |
| College of Arts | Fine Arts | 200000.0 | Oil Painting |
+------------------------+------------------------+-------------------+--------------------*/
La función GRAPH_EXPAND produce la tabla compactada aplicando una serie
de operaciones LEFT JOIN a las tablas de nodos y aristas. Si no especificas un nodo de inicio, el grafo de entrada debe tener una sola tabla de nodo raíz en el grafo de relaciones de esquema.
De manera equivalente, la tabla del nodo raíz debe tener una relación de varios a uno con
todas las demás tablas de nodos con las que comparte un borde.
Los datos a los que no se puede acceder desde la tabla del nodo raíz a través de una serie de uniones
no aparecen en el resultado. En el ejemplo anterior, Course es la tabla del nodo raíz. El departamento Research se omite del resultado porque no tiene ningún curso.
No puedes seleccionar directamente una columna para una propiedad definida por una medición.
En su lugar, debes unirlas en la
función AGG().
Esta función garantiza que la agregación definida en las mediciones se realice
exactamente una vez por clave.
La siguiente consulta calcula de forma simultánea el presupuesto total y la cantidad total de cursos para cada facultad:
SELECT
College_college_name,
AGG(Department_total_budget) AS college_budget,
AGG(Course_course_count) AS total_courses
FROM
GRAPH_EXPAND("university.SchoolGraph")
GROUP BY
College_college_name;
/*------------------------+----------------+---------------+
| College_college_name | college_budget | total_courses |
+------------------------+----------------+---------------+
| College of Engineering | 900000.0 | 3 |
| College of Arts | 200000.0 | 1 |
+------------------------+----------------+---------------*/
Usa un resultado jerárquico
Cuando especificas flatten_results => FALSE, GRAPH_EXPAND devuelve una
relación jerárquica en lugar de columnas con prefijo:
- Las propiedades del nodo raíz inicial aparecen como columnas de nivel superior sin prefijo
(por ejemplo,
course_nameocredits). - Las propiedades de las tablas vecinas unidas, que pueden ser tablas de nodos o de aristas, aparecen como campos
STRUCTanidados que llevan el nombre de la tabla de elementos. Por ejemplo, si el nodo de inicio esCourse, usarías la propiedad DeptCourse.Department.CollegeDept.College.college_nameto access thecollege_name`. - Las mediciones dentro de structs anidados se consultan con la notación de puntos dentro de
AGG(), por ejemplo,AGG(DeptCourse.Department.total_budget). - Si una tabla de elementos tiene una propiedad con el mismo nombre que una tabla de elementos
adyacente, no puedes consultar el grafo con
flatten_results => FALSE. En su lugar, debes compactar los resultados.
SELECT
DeptCourse.Department.dept_name AS department_name,
DeptCourse.Department.CollegeDept.College.college_name,
AGG(DeptCourse.Department.total_budget) AS total_budget,
AGG(course_count) AS total_courses
FROM GRAPH_EXPAND(
"university.SchoolGraph",
start_node => "Course",
flatten_results => FALSE)
GROUP BY department_name, college_name;
Trabaja con varias tablas de nodos raíz
Los grafos suelen contener varias tablas de nodos raíz, también llamadas tablas de hechos, que comparten tablas de dimensiones. Para consultar un grafo con varias tablas de nodos raíz, usa el argumento start_node en GRAPH_EXPAND. La función recorre
el subgrafo al que se puede acceder desde el nodo de inicio.
Por ejemplo, puedes agregar una tabla Instructor al conjunto de datos university que
también se vincule a Department:
CREATE OR REPLACE TABLE university.Instructor (
instructor_id INT64 PRIMARY KEY NOT ENFORCED,
instructor_name STRING,
dept_id INT64,
salary FLOAT64,
FOREIGN KEY (dept_id) REFERENCES university.Department(dept_id) NOT ENFORCED
);
INSERT INTO university.Instructor (instructor_id, instructor_name, dept_id, salary)
VALUES (501, 'Dr. Dana', 1001, 120000),
(502, 'Dr. Lee', 1001, 110000),
(503, 'Dr. Taylor', 1002, 115000),
(504, 'Prof. Izumi', 1003, 90000);
Puedes crear un gráfico nuevo llamado university.SchoolGraphMultiRoot que
incluya la tabla de nodos Instructor
y un borde de Instructor a Department:
CREATE OR REPLACE PROPERTY GRAPH university.SchoolGraphMultiRoot
NODE TABLES (
university.College
KEY(college_id)
PROPERTIES(college_id, college_name),
university.Department
KEY(dept_id)
PROPERTIES(dept_id, dept_name, college_id, budget,
MEASURE(SUM(budget)) AS total_budget),
university.Course
KEY(course_id)
PROPERTIES(
course_id, course_name, credits, dept_id,
MEASURE(COUNT(course_id)) AS course_count),
university.Instructor
KEY(instructor_id)
PROPERTIES(
instructor_id, instructor_name, dept_id, salary,
MEASURE(COUNT(instructor_id)) AS instructor_count,
MEASURE(SUM(salary)) AS total_salary)
)
EDGE TABLES (
university.Department AS CollegeDept
SOURCE KEY (college_id) REFERENCES College (college_id)
DESTINATION KEY (dept_id) REFERENCES Department (dept_id),
university.Course AS DeptCourse
SOURCE KEY (dept_id) REFERENCES Department (dept_id)
DESTINATION KEY (course_id) REFERENCES Course (course_id),
university.Instructor AS DeptInstructor
SOURCE KEY (dept_id) REFERENCES Department (dept_id)
DESTINATION KEY (instructor_id) REFERENCES Instructor (instructor_id)
);
En este gráfico, Course y Instructor funcionan como tablas de nodos raíz que
se vinculan a Department y College. Puedes segmentar cualquiera de los subgrafos pasando start_node:
-- Expand the Course subgraph (Course -> Department -> College)
SELECT
College_college_name,
AGG(Course_course_count) AS total_courses
FROM GRAPH_EXPAND("university.SchoolGraphMultiRoot", start_node => "Course")
GROUP BY College_college_name;
-- Expand the Instructor subgraph (Instructor -> Department -> College)
SELECT
College_college_name,
AGG(Instructor_total_salary) AS total_faculty_salary
FROM GRAPH_EXPAND("university.SchoolGraphMultiRoot", start_node => "Instructor")
GROUP BY College_college_name;
Para analizar las métricas en varias tablas de hechos, consulta cada subgrafo con
GRAPH_EXPAND y únelos en sus dimensiones compartidas en SQL
estándar:
SELECT
C.College_college_name,
AGG(C.Course_course_count) AS total_courses,
AGG(I.Instructor_total_salary) AS total_faculty_salary
FROM GRAPH_EXPAND("university.SchoolGraphMultiRoot", start_node => "Course") AS C
INNER JOIN GRAPH_EXPAND("university.SchoolGraphMultiRoot", start_node => "Instructor") AS I
ON C.Department_dept_id = I.Department_dept_id
GROUP BY C.College_college_name;
Tanto AGG(C.Course_course_count) como AGG(I.Instructor_total_salary) calculan
sus agregaciones con precisión en sus respectivos niveles de detalle sin interferencias
de la unión.
Prácticas recomendadas
Cuando diseñes un gráfico con medidas, sigue estas prácticas recomendadas:
Define nodos y aristas en una sola tabla
Te recomendamos que uses la misma tabla para una definición de tabla de nodos y sus
definiciones de tabla de bordes de conexión. Reutilizar la tabla garantiza una
relación uno a uno (1:1) entre la tabla de nodos y la tabla de bordes,
lo que evita la distribución ramificada y garantiza que la función GRAPH_EXPAND no
ignore el borde como ambiguo.
Por ejemplo, si tienes una tabla de entrada Enrollment que contiene claves externas
que apuntan a las tablas Student y Course, define el nodo Enrollment y sus
aristas salientes con la tabla de entrada Enrollment.
Evita definir propiedades innecesarias en las tablas de bordes
Cuando definas una tabla de nodos y una tabla de bordes con la misma tabla física,
no uses la cláusula PROPERTIES en la tabla de bordes, a menos que haya información nueva
para capturar en el borde. Debido a que la tabla de nodos y la tabla de aristas usan
la misma tabla física, todas estas propiedades ya forman parte de la tabla de
nodos.
Definir propiedades redundantes en la tabla de bordes puede hacer que la función GRAPH_EXPAND
trate la tabla de bordes como una entidad independiente de la tabla de nodos
compartida. Para optimizar el rendimiento, especifica NO PROPERTIES en la definición de la tabla de aristas cuando no haya propiedades nuevas para capturar en la arista.
Modelar relaciones de varios a varios como tablas de nodos
Una relación de muchos a muchos (M:N) se produce cuando una fila de la tabla A puede
corresponder a varias filas de la tabla B, y una fila de la tabla B puede corresponder
a varias filas de la tabla A. Por ejemplo, un estudiante puede inscribirse en varios
cursos y un curso puede tener muchos estudiantes. Puedes separar una relación de varios a varios en dos relaciones de uno a varios con una tabla de unión intermedia. Por ejemplo, puedes definir una tabla Enrollment
que contenga claves externas que apunten a las tablas Student y Course. Si
modelas una tabla de unión intermedia como una tabla de aristas que conecta dos
tablas de nodos de dimensión en tu sentencia CREATE PROPERTY GRAPH, como en el
siguiente ejemplo, la
función GRAPH_EXPAND ignora estas aristas porque su relación es
ambigua: no es claramente de varios a uno o de uno a uno.
CREATE PROPERTY GRAPH university.SchoolGraph
NODE TABLES (
university.Student,
university.Course
)
EDGE TABLES (
-- Modeling the junction table as a standalone edge causes ambiguity
university.Enrollment AS StudentEnrollment
SOURCE KEY (student_id) REFERENCES Student (student_id)
DESTINATION KEY (course_id) REFERENCES Course (course_id)
);
Si se ignoran todos los bordes de un grafo, las llamadas a GRAPH_EXPAND o
BQ.SHOW_GRAPH_EXPAND_SCHEMA fallan con un error.
Para modelar correctamente una relación M:N para las mediciones, asciende la tabla de unión intermedia a una tabla de nodos. En esta estructura, la tabla de nodos de unión actúa como la única tabla de nodos raíz (una tabla de nodos con un grado de entrada de cero). Luego, define dos tablas perimetrales de muchos a uno (N:1) que compartan la tabla de unión para conectar N:1 a cada una de las tablas de nodos de dimensión:
CREATE PROPERTY GRAPH university.SchoolGraph
NODE TABLES (
university.Student KEY (student_id),
university.Course KEY (course_id),
-- Promote the junction table to a node table (acting as the single root node)
university.Enrollment KEY (enrollment_id)
)
EDGE TABLES (
-- Share (reuse) the Enrollment table to define N:1 edges to each dimension
university.Enrollment AS EnrollmentToStudent
SOURCE KEY (enrollment_id) REFERENCES Enrollment (enrollment_id)
DESTINATION KEY (student_id) REFERENCES Student (student_id),
university.Enrollment AS EnrollmentToCourse
SOURCE KEY (enrollment_id) REFERENCES Enrollment (enrollment_id)
DESTINATION KEY (course_id) REFERENCES Course (course_id)
);
En esta estructura, Enrollment funciona como la única tabla de nodo raíz
(grado de entrada de cero) que conecta N:1 con las tablas de dimensiones Student y Course (grado de entrada de uno). La función GRAPH_EXPAND puede compactar este gráfico
recorriendo hacia afuera desde Enrollment hasta Student y Course. Si
defines mediciones en cualquiera de estas tablas, puedes llamar a la función AGG en las
columnas de medición para calcular correctamente las agregaciones sin contar de más.
Elige cómo las mediciones controlan las filas que no coinciden
Cuando una fila no tiene coincidencia en una tabla de elementos conectados, las columnas de esa tabla
son NULL en el resultado de GRAPH_EXPAND. Por ejemplo, si un curso no tiene un valor dept_id, las columnas Department de esa fila son NULL. Si una
medida en el nodo Department incluye estas filas, depende de dónde escribas
la expresión escalar que agrega la medida.
Si escribes la expresión dentro de la función de agregación, esta se evalúa para cada fila del resultado expandido, incluidas las filas sin coincidencia.
Cuando la expresión devuelve un valor en lugar de NULL para una de estas filas,
ese valor contribuye al resultado.
Si defines la expresión como una propiedad y haces referencia a esa propiedad desde la medición, la expresión se evalúa en las filas de la tabla de nodos, y las filas sin coincidencia no contribuyen al resultado.
En el siguiente ejemplo, se define la misma expresión de ambas maneras en
university.Department:
university.Department
KEY(dept_id)
PROPERTIES(dept_id, dept_name, college_id, budget,
COALESCE(budget, 0) AS budget_or_zero,
-- Includes courses that have no department, as a budget of 0.
MEASURE(AVG(COALESCE(budget, 0))) AS avg_budget_inline,
-- Averages only departments that exist.
MEASURE(AVG(budget_or_zero)) AS avg_budget_property)
Ambas medidas reemplazan un presupuesto de NULL por 0. El formulario intercalado también reemplaza
los valores NULL producidos para un curso que no tiene departamento, por lo que ese curso
se promedia como un departamento con un presupuesto de 0. Elige el formulario que
coincida con lo que quieres medir.
Visualiza el esquema de GRAPH_EXPAND
Para ver el esquema de la tabla que muestra la función GRAPH_EXPAND
sin llamar a la función, usa el
procedimiento del sistema BQ.SHOW_GRAPH_EXPAND_SCHEMA:
DECLARE schema STRING DEFAULT '';
CALL BQ.SHOW_GRAPH_EXPAND_SCHEMA('university.SchoolGraph', schema);
SELECT schema;
Este procedimiento propaga la variable schema con el nombre, el tipo y el modo
de cada columna. También indica si la propiedad a la que hace referencia la columna
es una medición y enumera cualquier descripción o sinónimo que hayas definido en ella.
¿Qué sigue?
- Aprende a crear y consultar un grafo.
- Obtén más información sobre los esquemas de grafos.