Pregunta

Tengo una consulta SQL (MSSQLSERVER) donde agrego columnas al conjunto de resultados usando subselecciones:

SELECT P.name, 
(select count(*) from cars C where C.type = 'sports') AS sportscars,
(select count(*) from cars C where C.type = 'family') AS familycars,
(select count(*) from cars C where C.type = 'business') AS businesscars
FROM people P
WHERE P.id = 1;

La consulta anterior es solo de una configuración de prueba que es un poco absurda, pero sirve bastante bien como ejemplo, creo. La consulta en la que realmente estoy trabajando abarca varias tablas complejas que solo distraen del problema en cuestión.

En el ejemplo anterior, cada registro en la tabla "personas" también tiene tres columnas adicionales: '' wantsSportscar '', '' wantsFamilycar '' y " wantsBusinesscar " ;. Ahora lo que quiero hacer es solo hacer la subselección de cada columna adicional si el respectivo " quiere ..... " campo en la tabla de personas se establece en "verdadero". En otras palabras, solo quiero hacer la primera subselección si P.wantsSportscar está establecido en verdadero para esa persona específica. La segunda y tercera subselecciones deberían funcionar de manera similar.

Entonces, la forma en que esta consulta debería funcionar es que muestra el nombre de una persona específica y la cantidad de modelos disponibles para los tipos de automóviles que quiere tener. Vale la pena señalar que mi conjunto de resultados final siempre contendrá un solo registro, es decir, el de un usuario específico.

Es importante que si una persona no está interesada en cierto tipo de automóviles, que la columna para ese tipo no se incluya en el conjunto de resultados final. Un ejemplo para asegurarse de que esto esté claro:

Si la persona A quiere un auto deportivo y un auto familiar, el resultado incluiría las columnas '' nombre '', '' autos deportivos '' y "familycars".

Si la persona B quiere un automóvil comercial, el resultado incluiría las columnas '' nombre '' y "businesscar".

He intentado usar varias combinaciones con sentencias IF, CASE y EXISTS, pero hasta ahora no he podido obtener una solución sintácticamente correcta. ¿Alguien sabe si esto es posible? Tenga en cuenta que la consulta se almacenará en un procedimiento almacenado.

¿Fue útil?

Solución

En su caso, existen posibles diseños de columna 8 y para hacer esto, necesitará 8 consultas separadas (o construir su consulta dinámicamente).

No es posible cambiar el diseño del conjunto de resultados dentro de una sola consulta.

En su lugar, puede diseñar su consulta de la siguiente manera:

SELECT  P.name, 
        CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars,
        CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars,
        CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars
FROM    people P
WHERE   P.id = 1

que seleccionará NULL en la columna apropiada si una persona no lo quiere, y analizará estos NULL en el lado del cliente.

Tenga en cuenta que el modelo relacional responde las consultas en términos de relaciones .

En su caso, la relación es la siguiente: "esta persona necesita estar satisfecha con tantos autos deportivos, tantos autos de negocios y tantos autos familiares".

El modelo relacional siempre responde a esta pregunta específica con una relación cuaternaria.

No omite ninguno de los miembros de la relación: en su lugar, solo los establece en NULL , que es la forma de SQL para mostrar que el miembro de un relación no definida, aplicable o significativa.

Otros consejos

Soy principalmente un tipo de Oracle, pero existe una alta probabilidad de que lo mismo se aplique. A menos que haya entendido mal, lo que quiere no es posible a ese nivel: siempre tendrá un número estático de columnas. Su consulta puede controlar si la columna está vacía, pero dado que en la parte más externa de la consulta ha especificado X número de columnas, tiene la garantía de obtener X columnas en su conjunto de resultados.

Como dije, no estoy familiarizado con MS SQL Server, pero supongo que habrá alguna forma de ejecutar SQL dinámico, en cuyo caso debe investigar eso, ya que debería permitirle construir una consulta más flexible.

Puede hacer lo que quiera seleccionando primero los valores como filas separadas en una tabla temporal, luego haciendo un PIVOT en esa tabla (convirtiendo las filas en columnas).

  

Es importante que si una persona no es   interesado en cierto tipo de autos,   que la columna para ese tipo no   ser incluido en el conjunto de resultados final. Un   ejemplo para asegurarse de que esto esté claro:

No podrá hacerlo en SQL simple. Le sugiero que haga que esta columna sea NULL o ZERO.

Si desea que la consulta se expanda dinámicamente cuando se agregan autos nuevos, entonces PIVOTing podría ayudarlo un poco.

Hay tres fundamentos que desea aprender para facilitar este trabajo. El primero es la normalización de datos, el segundo es GROUP BY y el tercero es PIVOT.

Primero, normalización de datos. Su diseño de la tabla de personas no está en la primera forma normal. Las columnas `` wantsports '', `` wantfamily '', `` wantbusiness '' son realmente un grupo repetitivo, aunque puede que no se vean como uno. Si puede modificar el diseño de la tabla, encontrará ventajoso crear una tercera tabla, llamémosla "peoplewant", con dos columnas clave, personid y cartype. Puedo entrar en detalles sobre por qué este diseño será más flexible y poderoso si lo desea, pero voy a omitir eso por ahora.

En GROUP BY. Esto le permite producir un resultado que resume cada grupo en una fila del resultado.

SELECT 
    p.name, 
    c.type, 
    c.count(*) as carcount
FROM people p, 
   INNER JOIN peoplewant pw ON p.id = pw.personid 
   INNER JOIN cars c on pw.cartype = c.type
WHERE
   p.id = 1
GROUP BY 
   p.name,
   c.type

Esta consulta (no probada) le brinda el resultado que desea, excepto que el resultado tiene una fila separada para cada tipo de automóvil que la persona desea.

Finalmente, PIVOTE. La herramienta PIVOT en su DBMS le permite convertir este resultado en un formulario donde solo hay una fila para la persona, y hay una columna separada para cada uno de los cartipos que esa persona desea. No he usado PIVOT yo mismo, así que dejaré que alguien más edite esta respuesta para proporcionar un ejemplo usando PIVOT.

Si usa la misma técnica para recuperar datos para varias personas en un barrido, tenga en cuenta que aparecerá una columna para cada tipo deseado que cualquier persona desee, y aparecerán ceros en el resultado PIVOT para personas que no desean un tipo de automóvil que está en las columnas de resultados.

Acabo de encontrar esta publicación a través de una búsqueda en Google, así que me doy cuenta de que llego un poco tarde a esta fiesta, pero ... seguro que esto es posible ... sin embargo, yo no sugeriría hacerlo de esta manera porque generalmente se considera una cosa muy mala (tm).

Dynamic SQL es tu respuesta.

Antes de decir cómo hacerlo, quiero hacer un prefacio con esto, Dynamic SQL es una cosa muy peligrosa, si no está desinfectando su entrada de la aplicación.

Por lo tanto, proceda con precaución:

declare @sqlToExecute nvarchar(max);
declare @includeSportsCars bit;
declare @includeFamilyCars bit;
declare @includeBusinessCars bit;

set @includeBusinessCars = 1
set @includeFamilyCars = 1
set @includeSportsCars  = 1

set @sqlToExecute = 'SELECT P.name '

if @includeSportsCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, ';
if @includeFamilyCars = 1
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, ';
if @includeBusinessCars = 1
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars '

set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;';

exec(@sqlToExecute)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top