Как преобразовать отношение «многие ко многим» в табличное представление в SQL?

#sql #sql-server #vb.net #visual-studio #many-to-many

#sql #sql-server #vb.net #visual-studio #многие ко многим

Вопрос:

Пожалуйста, мне нужна помощь. Я создал таблицу Users (столбцы UserID , UserName ) и таблицу Project (столбцы ProjectID , ProjectName ) и таблицу мостов, в которой отображаются имя проекта и имя пользователя.

В каждом проекте более одного пользователя, и каждый пользователь может участвовать в нескольких проектах.

Таблица мостов выглядит следующим образом:

введите описание изображения здесь

Доступно ли такое представление даты и как?

введите описание изображения здесь

Обратите внимание, что проекты и имена пользователей не являются фиксированными, они динамичны, они добавляются из форм в VB.NET .

Комментарии:

1. tutorialspoint.com/sql/sql-using-joins.htm

2. @michael raouf у вас определено количество столбцов? т.е. сколько максимальных пользователей может иметь один проект?

3. Нет максимального количества пользователей, мне нужно быть динамичным, я думаю, что это не логично, но я пытаюсь, доступно ли это

4. Я просто хочу просмотреть всех пользователей в одном проекте в простом представлении

Ответ №1:

могу ли я предложить следующее решение (пожалуйста, не судите слишком строго, это просто идея): вместо того, чтобы использовать фиктивное «Имя пользователя» в качестве имени столбца в вашей первой строке, почему бы не поместить туда реальные имена пользователей и иметь метки «да» / «нет» при пересечении строки / столбца для каждого пользователя /проект. Смотрите рисунок ниже. Это обычный тип представления в различных таблицах членства или таблицах подсчета очков игроков (но они имеют одинаковые имена по вертикали и горизонтали).

Предлагаемый динамический вид членства в проекте

Я вставил тестовые записи о членстве, чтобы вы видели, что Марк является участником во всех проектах, а Брайан — только в SQL.

Этот динамический SQL выдает приведенный выше результат:

 DECLARE @dynPivotSQL AS NVARCHAR(MAX)
DECLARE @pivotColNames AS NVARCHAR(MAX)
DECLARE @pivotColNamesMin AS NVARCHAR(MAX)

-- get distinct username for column headers
SELECT @pivotColNames= ISNULL(@pivotColNames   ',','') 
         QUOTENAME(UserName),
       @pivotColNamesMin= ISNULL(@pivotColNamesMin   ',','') 
         'min('   QUOTENAME(UserName)  ') as '   QUOTENAME(UserName)
FROM (SELECT DISTINCT UserName, UserID FROM Users) AS U

SET @dynPivotSQL = N'
with cte as (
    select p.ProjectName,u.UserName,IsMember=1 from ProjectsUsers pu
    join Projects p on p.ProjectID = pu.FK_ProjectID
    join Users u on u.UserID = pu.FK_UserID
) select ProjectName, '   @pivotColNamesMin   ' from cte
pivot (
    min(IsMember) for UserName in ('   @pivotColNames   ')
) pvt
group by ProjectName '          

EXEC sp_executesql @dynPivotSQL
  

Обратите внимание, поскольку вашим требованием является наличие динамических списков проектов / пользователей, мне пришлось использовать динамический sql для создания динамического списка столбцов для T-SQL PIVOT, который обычно работает с жестко заданными / статическими значениями / именами столбцов.

Обратите внимание, что в используемом блоке pivot также есть трюк min(IsMember), поскольку для pivot требуется некоторая включенная агрегатная функция, но мы не можем использовать min(UserName), потому что он действительно вернет минимальное имя в наборе конкретных имен участников проекта.

@pivotColNamesMin — это еще один трюк, теперь для GROUP BY, потому что он не будет выполняться, если все возвращаемые столбцы не находятся в списке group by или не находятся внутри агрегатной функции.

Я уверен, что вы можете передать предлагаемый источник данных в свою таблицу данных, и во время некоторых событий ItemDataBound или подобных событий вы можете изменить эти метки «1» на пользовательские строки или изображения с помощью «YES» или что-нибудь в этом роде. Но для того, чтобы это работало, ваша databrid не должна использовать жестко заданные имена столбцов и иметь возможность динамически загружать их из фактического набора результатов.

Я протестировал свой запрос на SQL Server 2008 R2. Дайте мне знать, является ли это приемлемым решением, чтобы я мог прекратить его настройку.

HTH

Ответ №2:

Спасибо, ребята, я нашел другое решение, выбрав название проекта из списка со списком, а в таблице отобразите имена пользователей, которые они участвуют в этих проектах, это не похоже на то, что я хочу в первом, но это решение более простое: D еще раз спасибо

Комментарии:

1. спасибо за обратную связь! Обратите внимание, что в будущем, если вам нужно превратить строки в столбцы, используйте предложение PIVOT и основывайтесь на нем.

Ответ №3:

Я создал таблицу в SQL Server с показанными вами записями и VB.NET консольное приложение с этим кодом:

 Option Infer On
Imports System.Data.SqlClient

Module Module1

    Sub Main()
        Dim csb As New SqlConnectionStringBuilder With {.DataSource = ".SQLEXPRESS", .InitialCatalog = "testing", .IntegratedSecurity = True}
        Dim projectsUsers As New Dictionary(Of String, List(Of String))
        Using sqlConn As New SqlConnection(csb.ConnectionString)
            Dim sql = "SELECT [project], [user] FROM ProjectUser"
            Using sqlCmd As New SqlCommand(sql, sqlConn)
                sqlConn.Open()
                Using rdr = sqlCmd.ExecuteReader()
                    If rdr.HasRows Then
                        While rdr.Read()
                            Dim project = rdr.GetString(0)
                            Dim user = rdr.GetString(1)
                            If projectsUsers.ContainsKey(project) Then
                                projectsUsers(project).Add(user)
                            Else
                                projectsUsers.Add(project, New List(Of String) From {user})
                            End If
                        End While
                    End If
                End Using
            End Using
        End Using

        ' adjust output as required
        For Each kvp In projectsUsers
            Console.WriteLine(kvp.Key amp; ": " amp; String.Join(", ", kvp.Value))
        Next

        Console.ReadLine()

    End Sub

End Module
  

и получил этот вывод:

SQL: Майкл, Сэм, Марк
C #: Сэм, Марк
Joomla: Марк

Вам просто нужно изменить метод для отображения выходных данных по желанию.

Комментарии:

1. Спасибо, Эндрю, я вижу, вы используете VB.net чтобы получить результат, то, что я ищу, чтобы сделать это с помощью SQL-запроса, чтобы загрузить его в datatable, а затем добавить этот datatable в datagridview, мне легко это сделать, но я просто хочу знать, может ли это быть прямым в sql какзапрос?

2. @michaelraouf Пожалуйста, отредактируйте свой вопрос, чтобы задать то, что вы на самом деле хотите спросить.