29th Nov 2024 Lectura de 18 minutos Proyecto SQL para principiantes: Cuadro de mando de ventas AdventureWorks Ekre Ceannmor Proyecto SQL ejercicio de SQL Índice Explorando la Base de Datos AdventureWorks Especificación del proyecto SQL ¿Qué incluiremos en nuestro Tablero de Proyecto SQL? Informe 1: Ventas Mensuales Pregunta: ¿Cuáles son los ingresos mensuales de la empresa? Enfoque Informe 2: Ingresos mensuales por país Pregunta: ¿Cuáles son los ingresos mensuales de cada país? Enfoque Informe 3: Productos más vendidos Pregunta: ¿Cuáles son nuestros productos más vendidos? Enfoque Informe 4: Tiendas con mejores resultados Pregunta: ¿Cuáles son las 10 tiendas que más han vendido en los últimos 2 meses? Enfoque Informe 5: Fuentes de ingresos Pregunta: ¿Cómo se comparan los ingresos en línea con los ingresos fuera de línea? Enfoque Informe 6: Tamaño medio de los pedidos por país Pregunta: ¿Cuál es el tamaño medio de los pedidos? Enfoque Informe 6: Valor medio del cliente durante su vida útil por región Pregunta: ¿Cuál es el valor medio de vida del cliente en cada región? Enfoque ¡Cree su Propio Proyecto SQL para Principiantes! Crear un proyecto SQL por su cuenta es una excelente forma de perfeccionar sus habilidades y adquirir experiencia práctica. Este ejemplo de proyecto SQL para principiantes le mostrará cómo crear un cuadro de mando de ventas con SQL y la base de datos de muestra AdventureWorks. Si usted es un principiante en SQL que desea llevar sus habilidades más allá de simples ejercicios de consulta, es una buena idea abordar un proyecto de análisis de datos SQL. No sólo le desafiará a aplicar lo que ha aprendido, sino que también le proporcionará una comprensión más profunda de cómo funciona SQL en escenarios del mundo real. Esta es tu oportunidad de pasar de la práctica básica a la creación de algo tangible que muestre tu creciente experiencia. En este artículo, te guiaremos a través de los pasos para crear un proyecto SQL utilizando la base de datos AdventureWorks. Cubriremos todo, desde la idea del proyecto hasta la escritura de las consultas finales. Aprenderás cómo abordar cada paso del proceso, y los conceptos que utilizaremos pueden transferirse a cualquier proyecto SQL. Una vez que hayas terminado, echa un vistazo a nuestro curso Bases de datos SQL para la práctica, que cuenta con 6 bases de datos diferentes para que practiques creando informes. También puedes leer cómo encontrar conjuntos de datos gratuitos para tu propio proyecto SQL y seguir con tu propia base de datos. Ahora, echemos un vistazo a la base de datos que utilizaremos en este proyecto SQL de ejemplo. Explorando la Base de Datos AdventureWorks La base de datos AdventureWorks es una base de datos de ejemplo creada por Microsoft para SQL Server. Ha sido portado a muchas bases de datos - incluyendo PostgreSQL, la cual usaremos en este artículo. La base de datos incluye 68 tablas que describen a un fabricante ficticio de bicicletas y contiene datos sobre diferentes tipos de transacciones que ocurren durante las operaciones comerciales. Como AdventureWorks es tan grande, es un conjunto de datos perfecto para principiantes que quieran practicar SQL en un entorno real. La base de datos está dividida en 5 esquemas, cada uno de los cuales representa un campo de operaciones diferente: Production, Purchasing, Sales, HR, y Person. En este proyecto, la mayoría de nuestros datos procederán del Sales . También utilizaremos otros esquemas para obtener información adicional. Veamos las tablas que más utilizaremos: SalesOrderHeader: Esta es la tabla más grande de la base de datos. Almacena toda la información relativa a un pedido en su conjunto. Será el punto de partida más común para las consultas relacionadas con las ventas. Product: En ella se almacena amplia información sobre los productos que ofrece la empresa. SalesOrderDetail: Esta tabla conecta los SalesOrderHeader y Product almacenando información sobre los productos individuales que componen cada pedido. ProductReview: Almacena las opiniones de los clientes sobre productos específicos. Store: Esta tabla almacena información básica sobre cada tienda. La mayoría de los datos se almacenan en la columna Demographics en formato XML; no la utilizaremos en este proyecto. SalesTerritory y CountryRegion: Utilizaremos estas dos tablas juntas para obtener el nombre del país al que está asociado el pedido. Hay muchas otras tablas en la base de datos, pero no se preocupe; iremos introduciéndolas y sus columnas importantes a medida que sea necesario. Especificación del proyecto SQL En este proyecto, queremos construir un tablero de ventas para AdventureWorks. Pero, ¿cómo empezamos un proyecto como éste? Un buen punto de partida es determinar el alcance del proyecto. A menudo, esto significa crear una lista de preguntas que desea responder con los datos. Nuestra lista inicial de preguntas es ¿Cuáles son las ventas mensuales totales? ¿Cuáles son las ventas mensuales por país? ¿Qué productos son los más vendidos? ¿Qué tiendas funcionan mejor? ¿Cuál es el volumen medio de cada pedido? ¿Cuál es el valor medio del ciclo de vida del cliente en cada país? A medida que nos sumergimos en los datos y escribimos nuestras consultas, podemos refinar estas preguntas y ajustar nuestras consultas en consecuencia. Así es como deberías empezar todos tus proyectos SQL: escribe una lista de las preguntas que tienes para los datos y luego escribe las consultas que te darán las respuestas. A medida que trabajes con las consultas y los datos, surgirán nuevas preguntas. Intenta responderlas también con tus consultas. Ahora estamos listos para empezar a escribir las consultas para nuestro panel de ventas. Tenga a mano nuestra hoja de ayudaSQL para principiantes en caso de que necesite un rápido repaso de la sintaxis. ¿Qué incluiremos en nuestro Tablero de Proyecto SQL? Informe 1: Ventas Mensuales Pregunta: ¿Cuáles son los ingresos mensuales de la empresa? El primer informe de nuestro cuadro de mandos muestra los ingresos de todos los meses anteriores (de todos los años). Esta consulta se puede utilizar para trazar líneas de tendencia de los ingresos, o se puede analizar tal cual. Queremos ver los datos en orden cronológico, empezando por las fechas más recientes. Enfoque En primer lugar, elegimos la tabla en la que se basará nuestra consulta. Queremos mostrar tres columnas: el año y el mes de la venta y los ingresos totales de ese mes y ese año. Necesitamos el importe total del pedido y la fecha de venta, por lo que la tabla SalesOrderHeader del esquema Sales es una opción obvia. Para mostrar el año y el mes, utilizaremos la función EXTRACT: EXTRACT(<MONTH / YEAR> FROM OrderDate) Utilizaremos estas columnas al agrupar y ordenar el resultado. Para obtener los ingresos totales de ese mes concreto, podemos utilizar SUM(TotalDue) junto con la agrupación por las dos columnas anteriores. Esto nos llevará a tener resultados SUM() separados para cada par año-mes único. Esta es la consulta final: SELECT EXTRACT(YEAR FROM OrderDate) AS OrderYear, EXTRACT(MONTH FROM OrderDate) AS OrderMonth, ROUND(SUM(TotalDue), 2) AS TotalRevenue FROM Sales.SalesOrderHeader GROUP BY EXTRACT(YEAR FROM OrderDate), EXTRACT(MONTH FROM OrderDate) ORDER BY OrderYear DESC, OrderMonth DESC; Informe 2: Ingresos mensuales por país Pregunta: ¿Cuáles son los ingresos mensuales de cada país? Queremos ver los ingresos totales de cada mes (de cada año) para cada país de la base de datos. El resultado debe ordenarse cronológicamente, con las fechas más recientes en primer lugar. Podemos utilizar estos datos para trazar líneas de tendencia para cada país o analizar los datos tal cual. Enfoque Esta consulta es similar a la anterior: sólo tenemos que añadir información sobre el país para cada compra. ¿Cómo podemos hacerlo? La tabla SalesOrderHeader tiene una columna llamada TerritoryId, que hace referencia a la tabla SalesTerritory tabla. Esta tabla nos da acceso a la columna CountryRegionCode. Los nombres de los países se almacenan en la tabla CountryRegion del Person del esquema. Podemos unir esta tabla a la tabla SalesTerritory utilizando el código de región. Esto nos deja una forma clara de ampliar nuestra consulta. Si unimos las tablas de la siguiente manera: SalesOrderHeader -> SalesTerritory -> CountryRegion, tendremos acceso a la columna CountryRegion.Name. Podemos añadirla como primera columna en la sentencia SELECT y añadirla al final de las sentencias GROUP BY y ORDER BY. Eche un vistazo a la consulta final: SELECT cr.Name AS Country, EXTRACT(YEAR FROM OrderDate) AS OrderYear, EXTRACT(MONTH FROM OrderDate) AS OrderMonth, ROUND(SUM(TotalDue), 2) AS TotalRevenue FROM Sales.SalesOrderHeader soh JOIN Sales.SalesTerritory st ON soh.TerritoryId = st.TerritoryId JOIN Person.CountryRegion cr ON cr.CountryRegionCode = st.CountryRegionCode GROUP BY cr.Name, EXTRACT(YEAR FROM OrderDate), EXTRACT(MONTH FROM OrderDate) ORDER BY OrderYear DESC, OrderMonth DESC, Country; He aquí una parte del resultado: CountryOrderYearOrderMonthTotalRevenue Australia201469958.82 Canada2014611471.62 France201463660.88 Germany201463818.44 Informe 3: Productos más vendidos Pregunta: ¿Cuáles son nuestros productos más vendidos? Nos gustaría añadir una lista de los productos más vendidos junto con alguna información relevante como las ventas de toda la vida y las opiniones de los clientes. El informe debe mostrar datos razonablemente recientes. Enfoque La tabla SalesOrderDetail que contiene la cantidad de cada artículo vendido, será nuestro punto de partida para esta consulta. La iremos construyendo poco a poco. La métrica principal de esta consulta es la cantidad total vendida de cada producto, que podemos obtener utilizando la combinación de SUM(OrderQty) y GROUP BY ProductId. Sin embargo, el resultado de la consulta sería bastante difícil de leer, ya que los productos sólo son identificables por su Id. Los nombres de los productos se almacenan en la tabla Product del esquema Production esquema. Uniendo las dos tablas mediante ProductId y añadiendo ProductName obtenemos esta consulta: SELECT p.ProductId, p.Name AS ProductName, SUM(od.OrderQty) AS TotalUnitsSold FROM Sales.SalesOrderDetail od JOIN Production.Product p ON od.ProductID = p.ProductID GROUP BY p.Name, p.ProductId ORDER BY TotalUnitsSold DESC LIMIT 10; ProductIdProductNameTotalUnitsSold 712AWC Logo Cap8311 870Water Bottle - 30 oz.6815 Esta consulta es un elemento viable del cuadro de mando, pero puede mejorarse. Ya hemos sentado las bases para trabajar con productos. Ahora, queremos añadir las valoraciones a nuestros productos. Vamos a construirlo como una consulta separada, que luego uniremos con la consulta principal. Las valoraciones de los productos se almacenan en la tabla ProductReview de la tabla Production esquema. Nos interesan principalmente ProductId y Rating dejadas por el cliente, así que promediaremos las puntuaciones de las valoraciones y agruparemos los datos por ProductId. Ahora tenemos una correspondencia unívoca entre el producto y sus valoraciones. Vamos a redondear el resultado a un decimal, como es habitual en las revisiones: SELECT ProductId, ROUND(AVG(Rating), 1) AS ProductRating FROM Production.ProductReview GROUP BY ProductId; ProductIdProductRating 9373.0 7985.0 7095.0 Ahora podemos envolver esta consulta en una expresión común de tabla (CTE) para utilizarla junto con nuestra consulta principal. Las CTE son una forma de crear un conjunto de resultados temporal, como una tabla virtual que sólo existe en el contexto de la consulta. Si quieres una explicación completa de cómo funcionan las CTEs, consulta nuestra guía completa sobre CTEs en SQL. Ahora bien, aquí hay un posible problema: puede que no haya reseñas para todos los productos. Por lo tanto, cuando unimos Product de la consulta principal con la CTE, asegúrese de utilizar LEFT JOIN para no perder ningún dato. Podría ser beneficioso mirar sólo los datos más recientes. Podemos filtrar las ventas antiguas en la consulta principal utilizando la columna OrderDate y restar dos meses de la fecha actual utilizando INTERVAL ‘2 MONTHS’. Entonces podemos filtrar los datos para mostrar sólo los pedidos realizados después de esa fecha. Si decide cambiar el intervalo de fechas, modificar la parte INTERVAL es bastante sencillo. Para aprender más sobre la sintaxis de INTERVAL, eche un vistazo a nuestro curso Funciones estándar de SQL . Esta es la consulta completa: WITH ProductRating AS ( SELECT ProductId, ROUND(AVG(Rating), 1) AS ProductRating FROM Production.ProductReview GROUP BY ProductId ) SELECT p.ProductId, p.Name AS ProductName, SUM(od.OrderQty) AS TotalUnitsSold, pr.ProductRating AS ProductRating FROM Sales.SalesOrderDetail od JOIN Sales.SalesOrderHeader oh ON od.SalesOrderId = oh.SalesOrderId JOIN Production.Product p ON od.ProductID = p.ProductID LEFT JOIN ProductRating pr ON pr.ProductId = p.ProductId WHERE OrderDate > CURRENT_DATE - INTERVAL '2 MONTH' GROUP BY p.Name, p.ProductId, pr.ProductRating ORDER BY TotalUnitsSold DESC LIMIT 10; ProductIdProductNameTotalUnitsSoldProductRating 921Mountain Tire Tube187N/A 873Patch Kit / 8 Patches181N/A 870Water Bottle - 30 oz.168N/A 922Road Tire Tube160N/A 878Fender Set - Mountain107N/A 711Sport-100 Helmet, Blue96N/A 712AWC Logo Cap95N/A 708Sport-100 Helmet, Black93N/A 923Touring Tire Tube91N/A 871Mountain Bottle Cage84N/A Ésta sí que es una lista digna de aparecer en el panel de control de un representante. Conocer las tendencias de ventas es algo a lo que todos los empleados deberían tener acceso, y ahora tenemos una consulta que nos lo ofrece. Nuestro cuadro de mandos está creciendo; ¡añadamos algunas métricas más! Informe 4: Tiendas con mejores resultados Pregunta: ¿Cuáles son las 10 tiendas que más han vendido en los últimos 2 meses? Nos gustaría mostrar las diez principales tiendas -incluidas las tiendas en línea- y su respectivo aumento de ingresos en los últimos dos meses. Una "tienda líder" se define como la que más ingresos ha obtenido en los últimos 2 meses. Trate todas las tiendas en línea como una sola entidad. Enfoque Como de costumbre, empezaremos con la tabla SalesOrderHeader tabla. Para saber qué tienda es responsable de qué venta, echemos un vistazo a la tabla Store en el esquema Sales esquema. Cada tienda puede vincularse a una venta a través de la columna SalesPersonId. Todas las ventas en las que SalesPersonId es NULL también tienen su OnlineOrderFlag establecido en true. Esto facilitará el trabajo de agrupar y mostrar las ventas en línea, ya que podemos tratar cualquier venta en la que SalesPersonId sea NULL como una venta en línea. Para no perder datos al unir las tablas en SalesPersonId (que a veces es NULL), utilice un LEFT JOIN entre SalesOrderHeader y Tienda. En la sentencia SELECT, nos importan dos cosas: el nombre de la tienda y el total de ventas realizadas en esa tienda. No olvide envolver la columna Store.Name con COALESCE(..., ‘Online’) para asegurarse de que todos los pedidos en línea tengan una etiqueta legible. Para filtrar los datos antiguos, utilice la misma condición WHERE que en el último informe: WHERE OrderDate > CURRENT_DATE - INTERVAL ‘2 MONTHS’. Ordene los resultados por el importe total de las ventas (mostrando primero los valores más grandes) y utilice LIMIT 10 para devolver sólo las 10 tiendas principales por ingresos en el periodo seleccionado. Esta es la consulta: SELECT COALESCE(s.Name, 'Online') AS StoreName, ROUND(SUM(so.TotalDue), 2) AS TotalSalesAmount FROM Sales.SalesOrderHeader so LEFT JOIN Sales.Store s ON so.SalesPersonId = s.SalesPersonId WHERE OrderDate > CURRENT_DATE - INTERVAL '2 MONTHS' GROUP BY s.Name ORDER BY TotalSalesAmount DESC LIMIT 10; StoreNameTotalSalesAmount Online2419079.86 Friendly Bike Shop577136.64 Exceptional Cycles Services495918.62 Number One Bike Co.495918.62 Informe 5: Fuentes de ingresos Pregunta: ¿Cómo se comparan los ingresos en línea con los ingresos fuera de línea? Para esta métrica del cuadro de mando, nos gustaría incluir datos sobre cuántos pedidos se realizaron en línea frente a los realizados en una tienda. Los resultados finales deberían desglosarse por mes y año. Enfoque Una vez más, construiremos esta consulta de forma iterativa. Para dividir las ventas en grupos en línea y fuera de línea, podemos utilizar la dirección OnlineOrderFlag de la tabla Sales.SalesOrderHeader de la tabla. Esta tabla también incluye convenientemente el precio total del pedido, por lo que esta métrica puede calcularse a partir de una sola tabla. Sin embargo, OnlineOrderFlag es un dato de tipo BOOLEAN; si lo utilizamos en la cláusula GROUP BY nos quedaremos con los grupos true y false. Para que los datos sean más legibles, podemos utilizar una expresión CASE WHEN para sustituir todos los verdaderos por "Online" y todos los falsos por "Store". Y como OnlineOrderFlag ya evalúa a BOOLEAN, no necesitamos modificarla para utilizarla como condición. Para una explicación más detallada sobre cómo estamos utilizando CASE WHEN en este ejemplo, lea nuestra guía sobre cómo utilizar CASE WHEN con GROUP BY. Ahora que tenemos los nombres de grupo adecuados, podemos seleccionar las otras métricas que comparan las fuentes de ingresos: el número total de acuerdos cerrados y los ingresos totales de esos acuerdos. Puede encontrar el primero simplemente contando todos los SalesOrderIds y el segundo sumando todos los valores de TotalDue. La agrupación puede realizarse utilizando OnlineOrderFlag y no la expresión CASE WHEN; en este caso, simplemente estamos asignando valores. Este es el aspecto de la consulta actual: SELECT CASE WHEN OnlineOrderFlag THEN 'Online' ELSE 'Store' END AS OrderOrigin, COUNT(SalesOrderId) AS TotalSales, SUM(TotalDue) AS TotalRevenue FROM Sales.SalesOrderHeader GROUP BY OnlineOrderFlag ORDER BY TotalRevenue DESC; OrderOriginTotalSalesTotalRevenue Online276593244139 Store380690775446 Podemos ver claramente una conclusión: Las tiendas físicas realizan casi 10 veces menos ventas pero producen 3 veces más ingresos que la tienda online. Podemos hacer que estos datos sean más significativos filtrándolos por año y mes para poder ver las tendencias en los flujos de ingresos. La tabla Sales.SalesOrderHeader tabla incluye la columna OrderDate, que podemos utilizar para obtener sólo las partes YEAR y MONTH como hicimos en el primer informe. Si añadimos esto como columnas adicionales y las utilizamos en GROUP BY, veremos que los grupos “Store” y “Online” se han dividido en diferentes años y meses. Podemos filtrar adicionalmente las fechas, como hicimos antes con CURRENT_DATE y INTERVAL. Aquí está la consulta completa y una muestra de los resultados: SELECT CASE WHEN OnlineOrderFlag THEN 'Online' ELSE 'Store' END AS OrderOrigin, EXTRACT(YEAR FROM OrderDate) AS OrderYear, EXTRACT(MONTH FROM OrderDate) AS OrderMonth, COUNT(SalesOrderId) AS TotalSales, SUM(TotalDue) AS totalRevenue FROM Sales.SalesOrderHeader WHERE OrderDate > CURRENT_DATE - INTERVAL '2 MONTHS' GROUP BY EXTRACT(YEAR FROM OrderDate), EXTRACT(MONTH FROM OrderDate), OnlineOrderFlag ORDER BY OrderOrigin, OrderYear DESC, OrderMonth DESC; OrderOriginOrderYearOrderMonthTotalSalesTotalRevenue Online2014693954151,48 Store20145125843850111,69 Online201454062156071,52 Store2014421428,61 Informe 6: Tamaño medio de los pedidos por país Pregunta: ¿Cuál es el tamaño medio de los pedidos? Nos gustaría mostrar cada país y el tamaño medio de los pedidos de todos los pedidos procedentes de ese país. Los países con mayor volumen medio de pedidos deben mostrarse en primer lugar. Enfoque Ya hemos obtenido el nombre del país para cada venta en uno de los informes anteriores, así que podemos centrarnos en el tamaño de los pedidos. Es importante tener en cuenta que tendremos que agregar dos veces en esta consulta: Primero para obtener el tamaño de cada pedido y luego para obtener el promedio por país. Empezaremos por obtener los tamaños de los pedidos. Cada pedido puede identificarse mediante una entrada en la tabla SalesOrderHeader de la tabla. Tendrá varias entradas respectivas en la tabla SalesOrderDetail tabla correspondientes a cada producto del pedido. Si sumamos los OrderQtys de la tabla SalesOrderDetail tabla para cada SalesOrderId nos deja pares ordenados de número_pedido-tamaño_pedido. Utilizaremos un par de JOINs para obtener los nombres de los países, formando un CTE como éste: WITH OrderSizes AS ( SELECT sod.SalesOrderId, SUM(OrderQty) AS ProductCount, cr.Name AS Country FROM Sales.SalesOrderHeader soh JOIN Sales.SalesOrderDetail sod ON sod.SalesOrderId = soh.SalesOrderId JOIN Sales.SalesTerritory st ON soh.TerritoryId = st.TerritoryId JOIN Person.CountryRegion cr ON cr.CountryRegionCode = st.CountryRegionCode GROUP BY sod.SalesOrderId, cr.Name ) SELECT * FROM OrderSizes; Ahora sólo es cuestión de obtener el tamaño medio de los pedidos a partir del CTE y agrupar el resultado por países. No olvides ordenar los datos para mostrar primero los pedidos de mayor tamaño medio. Eche un vistazo a la consulta completa y a su resultado parcial: WITH OrderSizes AS ( SELECT sod.SalesOrderId, SUM(OrderQty) AS ProductCount, cr.Name AS Country FROM Sales.SalesOrderHeader soh JOIN Sales.SalesOrderDetail sod ON sod.SalesOrderId = soh.SalesOrderId JOIN Sales.SalesTerritory st ON soh.TerritoryId = st.TerritoryId JOIN Person.CountryRegion cr ON cr.CountryRegionCode = st.CountryRegionCode GROUP BY sod.SalesOrderId, cr.Name ) SELECT Country, ROUND(AVG(ProductCount), 2) AS AverageOrderSize FROM OrderSizes GROUP BY Country ORDER BY AverageOrderSize DESC; CountryAverageOrderSize United States12.80 Canada12.14 France7.45 United Kingdom6.24 Germany5.01 Australia2.67 Informe 6: Valor medio del cliente durante su vida útil por región Pregunta: ¿Cuál es el valor medio de vida del cliente en cada región? El valor del ciclo de vida del cliente (CLV) es una métrica importante que muestra cuánto puede esperar la empresa que gaste un cliente recién adquirido durante toda su relación con la marca. El CLV se calcula sumando los ingresos de todas las compras del cliente. Es muy útil para calcular el presupuesto de las campañas de marketing; se muestra mejor agrupado por la región de operaciones. Enfoque Primero vamos a crear la consulta que calcula el CLV medio de cada cliente individualmente. Ya sabemos cómo se calcula el CLV: sumando todos los valores de TotalDue para cada cliente de la tabla SalesOrderHeader de la tabla. Esto nos dará un resultado intermedio. Más adelante queremos agruparlo por países, por lo que es conveniente incluir aquí también algunos de esos datos. La tabla Customer en el mismo esquema tiene una columna TerritoryId, que podemos utilizar más adelante para obtener más información. De momento, vamos a añadir TerritoryId a la consulta CLV como columna adicional. Esto requiere una simple unión y una ampliación de la sentencia GROUP BY. Este es el aspecto de la consulta hasta ahora, junto con algunos resultados de ejemplo: SELECT cs.CustomerId, cs.TerritoryId, SUM(TotalDue) AS LifetimeRevenues FROM sales.Customer cs JOIN sales.SalesOrderheader ord ON cstm.CustomerId = ord.CustomerId GROUP BY cs.CustomerId, cs.TerritoryId; CustomerIdTerritoryIdLifetimeRevenue 26264434.56 30052421863.90 244161106.16 2674592135.37 La mitad del problema está resuelto. Lo que queda es agrupar esto a nivel de país y hacer que el resultado sea más legible. Utilicemos un CTE para guardar los resultados de esta consulta. Podemos agrupar el CTE por TerritoryId y mostrar el valor medio de vida de los clientes por territorio. Esto da un resultado técnicamente correcto, aunque los territorios no son identificables. Los nombres reales de los países se almacenan en la tabla CountryRegion de la tabla Person esquema. Es identificable por la clave CountryRegionCode. En el Sales podemos encontrar esa clave en la tabla SalesTerritory tabla. Así, uniremos el resultado de la CTE, a través de la tabla SalesTerritory con la tabla CountryRegion tabla. Ahora podemos mostrar el nombre del país en lugar de su numérico Id. No olvides editar el GROUP BY en consecuencia. Echa un vistazo a la consulta final y a algunos de sus resultados: WITH CustomerLifetimeRevenue AS ( SELECT cstm.CustomerId, ord.TerritoryId, SUM(TotalDue) AS LifetimeRevenue FROM Sales.Customer cstm JOIN Sales.SalesOrderHeader ord ON cstm.CustomerId = ord.CustomerId GROUP BY cstm.CustomerId, ord.TerritoryId ) SELECT cr.Name AS Country, ROUND(AVG(clr.LifetimeRevenue),2) AS AvgLifetimeCustomerValue FROM CustomerLifetimeRevenue clr JOIN Sales.SalesTerritory tr ON clr.TerritoryId = tr.TerritoryId JOIN Person.CountryRegion cr ON cr.CountryRegionCode = tr.CountryRegionCode GROUP BY cr.Name ORDER BY AvgLifetimeCustomerValue DESC, cr.Name; CountryAvgLifetimeCustomerValue Canada10971.34 United States8627.27 France4403.33 United Kingdom4394.69 Australia3259.14 Germany3024.18 ¡Cree su Propio Proyecto SQL para Principiantes! Hemos creado exitosamente consultas que pueden alimentar un tablero de ventas informativo. Sin embargo, el objetivo de este artículo es inspirarle para que inicie su propio proyecto SQL. Recapitulemos cómo crear un proyecto SQL: Encuentre un conjunto de datos interesante. Plantee un problema de negocio para este conjunto de datos, como hicimos con este cuadro de mando de ventas. Escriba preguntas relacionadas con el problema que el proyecto ayudará a responder. Construye consultas SQL para las preguntas que has escrito. Si necesitas un poco más de estructura, te recomendamos nuestro mini-curso Bases de Datos SQL para la Práctica. Contiene 6 bases de datos diferentes que puedes utilizar en tu propio proyecto SQL, así como algunas preguntas y sugerencias para empezar. Si quieres ver más ejemplos de proyectos SQL para principiantes, echa un vistazo a nuestro artículo sobre cómo crear un proyecto de tienda Northwind para tu portfolio. ¿Tienes hambre de más práctica general? Echa un vistazo a nuestro enorme Ejercicio de SQL Track. ¡Feliz aprendizaje! Tags: Proyecto SQL ejercicio de SQL