2008-11-18 16 views
6

Estoy trabajando con una base de datos existente y tratando de escribir una consulta sql para obtener toda la información de la cuenta, incluidos los niveles de permiso. Esto es para una auditoría de seguridad. Queremos descargar toda esta información de forma legible para que sea más fácil compararla. Mi problema es que hay una tabla puente/enlace para los permisos, por lo que hay múltiples registros por usuario. Quiero obtener resultados con todos los permisos para un usuario en una línea. Aquí hay un ejemplo:Ayuda de consulta SQL con la tabla de puente

Table_User: 
UserId UserName 
1  John 
2  Joe 
3  James 

Table_UserPermissions: 
UserId PermissionId Rights 
1  10    1 
1  11    2 
1  12    3 
2  11    2 
2  12    3 
3  10    2 

PermissionID se vincula a una tabla con el nombre del Permiso y lo que hace. La derecha es igual que 1 = Vista, 2 = modificar y etc.

Lo que vuelva de una consulta básica para el usuario 1 es:

UserId UserName PermissionId Rights 
1  John  10   1 
1  John  11   2 
1  John  12   3 

Lo que me gustaría algo como esto:

UserId UserName Permission1 Rights1 Permission2 Right2 Permission3 Right3 
1  John  10   1  11   2  12   3 

Idealmente, quisiera esto para todos los usuarios. Lo más parecido que he encontrado es la función Pivot en SQL Server 2005. http://geekswithblogs.net/lorint/archive/2006/08/04/87166.aspx El problema con esto por lo que puedo decir es que tengo que nombrar cada columna para cada usuario y no estoy seguro de cómo obtener el nivel de derechos Con datos reales, tengo alrededor de 130 usuarios y 40 permisos diferentes.

¿Hay alguna otra manera con sql que puedo hacer esto?

+0

I * saber * He visto casi exactamente esta pregunta en este sitio al menos dos veces. Pero no puedo encontrarlo –

Respuesta

3

se podría hacer algo como esto:

select userid, username 
,  max(case when permissionid=10 then rights end) as permission10_rights 
,  max(case when permissionid=11 then rights end) as permission11_rights 
,  max(case when permissionid=12 then rights end) as permission12_rights 
from userpermissions 
group by userid, username; 

Hay que añadir explícitamente una columna max similar (...) para cada permissionid.

+1

También vea la palabra clave PIVOT para SQL Server 2005 y posteriores –

0

Se puede crear un table_flatuserpermissions temporales de:

UserID 
PermissionID1 
Rights1 
PermissionID2 
Rights2 
...etc to as many permission/right combinations as you need

registros Insertar para esta tabla de Table_user con todos los permisos & campos de derechos nulo.

Actualizar registros en esta tabla de table_userpermissions - primer registro de inserción y establecer PermissionID1 & derechos1, segundo disco para una actualización de usuario PermissionsID2 & derechos2, etc.

A continuación, consultar esta tabla para generar el informe.

Personalmente, me quedaría con las columnas UserId, UserName, PermissionID, Rights que tiene ahora.

Quizás sustituir en algún texto por PermisoID y Derechos en lugar de los valores numéricos.

Quizás ordene la tabla por ID de permiso, usuario en lugar de usuario, ID de permiso para que el auditor pueda verificar los usuarios en cada tipo de permiso.

1

Respuesta corta:

No se puede agregar dinámicamente columnas en su consulta.

Recuerde, SQL es un lenguaje basado en conjunto. Consulta conjuntos y une conjuntos.

Lo que está descubriendo es una lista recursiva y que requiere que la lista se encadene horizontalmente en lugar de verticalmente.

Puede, así que, fingir, con un conjunto de autocombinaciones, pero para hacerlo, debe conocer todos los permisos posibles antes de escribir la consulta ... que es lo que las otras sugerencias han propuesto.

También puede volver a colocar el conjunto de registros en un idioma diferente y luego recorrerlo para generar las columnas adecuadas.

Algo así como:

SELECT Table_User.userID, userName, permissionid, rights 
FROM Table_User 
     LEFT JOIN Table_UserPermissions ON Table_User.userID =Table_UserPermissions.userID 
ORDER BY userName 

Y a continuación, mostrar todos los permisos para cada usuario usando algo como (Python):

userID = recordset[0][0] 
userName = recordset[0][1] 
for row in recordset: 
    if userID != row[0]: 
     printUserPermissions(username, user_permissions) 
     user_permissions = [] 
     username = row[1] 
     userID = row[0] 

    user_permissions.append((row[2], row[3])) 

printUserPermissions(username, user_permissions) 
+0

Gracias. Estoy pensando que tampoco va a funcionar con solo sql. No estoy sumando los valores de un campo como muestran la mayoría de los ejemplos dinámicos y hay demasiados campos de permisos para escribir columnas. El administrador tiene más de 120 permisos. Creo que tendré que codificarlo con C#. – MaxGeek

0

Si es aceptable, una estrategia que he usado, tanto para diseño y/o implementación, es volcar la consulta no iniciada en Excel o Access. Ambos tienen interfaces de usuario mucho más amigables para datos dinámicos, y mucha más gente se siente cómoda en ese entorno.

Una vez que tiene un diseño que le gusta, entonces es más fácil pensar en cómo duplicarlo en TSQL.

0

Parece que la función de pivote se diseñó para situaciones donde puede usar una función de agregado en uno de los campos. Como si quisiera saber cuántos ingresos gana cada persona de ventas para la empresa x. Podría resumir el campo de precios de una tabla de ventas. Luego obtendría la persona de ventas y la cantidad de ingresos en ventas que tienen. Para los permisos, aunque no tiene sentido sumar/contar/etc, sube el campo permisoId o el campo Derechos.

1

Si utiliza MySQL, le sugiero que utilice group_concat() como a continuación.

select UserId, UserName, 
     group_concat(PermissionId) as PermIdList, 
     group_concat(Rights SEPARATOR ',') as RightsList 
from Table_user join Table_UserPermissions on 
    Table_User.UserId = Table_UserPermissions.UserId= 
GROUP BY Table_User.UserId 

Esto volvería

UserId UserName PermIdList RightsList 
1  John  10,11,12 1,2,3 

Una rápida búsqueda en Google para 'GROUP_CONCAT mssql' reveló un par de diferentes procedimientos almacenados (I), (II) para MSSQL que pueden lograr el mismo comportamiento.

0

Para este tipo de transformación de datos deberá realizar un UNPIVOT y luego un PIVOT de los datos. Si conoce los valores que desea transformar, puede codificar la consulta mediante un pivote estático; de lo contrario, puede usar sql dinámico.

Crear tablas:

CREATE TABLE Table_User 
    ([UserId] int, [UserName] varchar(5)) 
; 

INSERT INTO Table_User 
    ([UserId], [UserName]) 
VALUES 
    (1, 'John'), 
    (2, 'Joe'), 
    (3, 'James') 
; 

CREATE TABLE Table_UserPermissions 
    ([UserId] int, [PermissionId] int, [Rights] int) 
; 

INSERT INTO Table_UserPermissions 
    ([UserId], [PermissionId], [Rights]) 
VALUES 
    (1, 10, 1), 
    (1, 11, 2), 
    (1, 12, 3), 
    (2, 11, 2), 
    (2, 12, 3), 
    (3, 10, 2) 
; 

PIVOT estática:

select * 
from 
(
    select userid, 
    username, 
    value, 
    col + '_'+ cast(rn as varchar(10)) col 
    from 
    (
    select u.userid, 
     u.username, 
     p.permissionid, 
     p.rights, 
     row_number() over(partition by u.userid 
         order by p.permissionid, p.rights) rn 
    from table_user u 
    left join Table_UserPermissions p 
     on u.userid = p.userid 
) src 
    unpivot 
    (
    value 
    for col in (permissionid, rights) 
) unpiv 
) src 
pivot 
(
    max(value) 
    for col in (permissionid_1, rights_1, 
       permissionid_2, rights_2, 
       permissionid_3, rights_3) 
) piv 
order by userid 

Ver SQL Fiddle with Demo

cruzada dinámica:

Si usted tiene un número desconocido de permissionid s y rights, a continuación, puede utilizar SQL dinámico:

DECLARE 
    @query AS NVARCHAR(MAX), 
    @colsPivot as NVARCHAR(MAX) 

select @colsPivot = STUFF((SELECT ',' 
         + quotename(c.name +'_'+ cast(t.rn as varchar(10))) 
        from 
        (
         select row_number() over(partition by u.userid 
           order by p.permissionid, p.rights) rn 
         from table_user u 
         left join Table_UserPermissions p 
         on u.userid = p.userid 
        ) t 
        cross apply sys.columns as C 
        where C.object_id = object_id('Table_UserPermissions') and 
         C.name not in ('UserId') 
        group by c.name, t.rn 
        order by t.rn 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 


set @query 
    = 'select * 
    from 
    (
     select userid, 
     username, 
     value, 
     col + ''_''+ cast(rn as varchar(10)) col 
     from 
     (
     select u.userid, 
      u.username, 
      p.permissionid, 
      p.rights, 
      row_number() over(partition by u.userid 
          order by p.permissionid, p.rights) rn 
     from table_user u 
     left join Table_UserPermissions p 
      on u.userid = p.userid 
     ) src 
     unpivot 
     (
     value 
     for col in (permissionid, rights) 
     ) unpiv 
    ) x1 
    pivot 
    (
     max(value) 
     for col in ('+ @colspivot +') 
    ) p 
    order by userid' 

exec(@query) 

Ver SQL Fiddle with demo

El resultado de ambos es:

| USERID | USERNAME | PERMISSIONID_1 | RIGHTS_1 | PERMISSIONID_2 | RIGHTS_2 | PERMISSIONID_3 | RIGHTS_3 | 
--------------------------------------------------------------------------------------------------------- 
|  1 |  John |    10 |  1 |    11 |  2 |    12 |  3 | 
|  2 |  Joe |    11 |  2 |    12 |  3 |   (null) | (null) | 
|  3 | James |    10 |  2 |   (null) | (null) |   (null) | (null) | 
Cuestiones relacionadas