【问题标题】:Query with multiple line results in only one line多行查询结果只有一行
【发布时间】:2015-09-09 17:11:24
【问题描述】:

我正在 asp.net 中开发一个学校项目,我试图得到一个查询,结果是几行(这是正确的),只在一行上显示每个“结果”的结果,事情是该项目是一个销售游戏的网页。每个游戏都有几个开发者、流派(动作、FPS 等)、格式(数字或物理)、平台(ps3、ps4 等)和其他类似的设置。我对其进行了测试,它返回了几行,正如我之前所说的,它们可以合成为具有多个列重复的值的单行。

这里是查询:

SELECT 
    dbo.Jogos.NomeJogo AS Jogo, 
    dbo.Plataforma.Plataforma,
    dbo.Jogos.disponibilidade, 
    dbo.Jogos.Preco AS Preço, 
    dbo.Jogos.Stock, 
    dbo.Desenvolvedora.NomeDesenvolvedora AS Desenvolvedora, 
    dbo.PEGI.PEGI, 
    dbo.Formato.Formato, 
    dbo.Genero.Genero, 
    dbo.Fornecedor.NomeFornecedor AS Fornecedor 
FROM dbo.Jogos 
    INNER JOIN dbo.Desenvolvedora ON dbo.Jogos.IdDesenvolvedora = dbo.Desenvolvedora.IdDesenvolvedora 
    INNER JOIN dbo.PEGI ON dbo.Jogos.IdPegi = dbo.PEGI.IdPEGI 
    INNER JOIN dbo.GeneroJogo ON dbo.Jogos.IdJogo = dbo.GeneroJogo.IdJogo 
    INNER JOIN dbo.Genero ON dbo.GeneroJogo.IdGenero = dbo.Genero.IdGenero 
    INNER JOIN dbo.JogosFormato ON dbo.Jogos.IdJogo = dbo.JogosFormato.IdJogo 
    INNER JOIN dbo.Formato ON dbo.JogosFormato.IdFormato = dbo.Formato.IdFormato 
    INNER JOIN dbo.JogosFornecedor ON dbo.Jogos.IdJogo = dbo.JogosFornecedor.IdJogo 
    INNER JOIN dbo.Fornecedor ON dbo.JogosFornecedor.IdFornecedor = dbo.Fornecedor.IdFornecedor 
    INNER JOIN dbo.JogosPlataforma ON dbo.Jogos.IdJogo = dbo.JogosPlataforma.IdJogo 
    INNER JOIN dbo.Plataforma ON dbo.JogosPlataforma.IdPlataforma = dbo.Plataforma.IdPlataforma

查询返回同一游戏的几行,可以恢复到只有一个。

例如:

Game              |  Genre   |  Platform  |  Developper
___________________________________________________________

Assassin's Creed  |  Action  |  Ps3       |  Ubisoft
Assassin's Creed  |  Stealth |  Ps3       |  Ubisoft
Assassin's Creed  |  Action  |  xBox 360  |  Ubisoft

我想获得类似的东西:

Game              |  Genre           |  Platform       |  Developper
_____________________________________________________________________

Assassin's Creed  |  Action, Stealth |  Ps3, Xbox 360  |  Ubisoft

我正在寻求帮助,因为我不明白这怎么可能。

任何帮助将不胜感激。

P.S.:查询中的一些值是葡萄牙语。 我也检查了这个线程 How to use GROUP BY to concatenate strings in SQL Server? 但是这个只使用一个表,我从几个表中获取数据,所以我真的不知道如何在这个表中进行。

提前谢谢你 最好的祝福, 凯文

【问题讨论】:

  • 您是否考虑过使用类似实体框架的 ORM?你所说的会做一个非常简单的 LINQ 查询,但是用直接 SQL 做起来有点 hacky,它喜欢把所有东西都放在漂亮的平面表中。

标签: c# sql asp.net sql-server


【解决方案1】:

有几种方法可以解决这个问题。 Lanorkin 在他/她的评论中引用的问题的公认答案显示了其中一个与 SQL Serer (XML PATH) 一起使用的问题。如果这听起来很深奥或令人恐惧,您可以使用关联子查询和用户定义的函数,为您连接值。

基本思想是,您有一个返回游戏的每个唯一值的外部查询和一个提取流派、平台或开发者的串联列表的内部查询。

create function getPlatforms (@jogoId nvarchar(50))
returns nvarchar(1024)
as 
begin
  declare @platforms nvarchar(1024);
  select @platforms = '';
  -- I understand that your Platform is normalized in your schema, but 
  -- you get the ide. Just make this a join. You can also strip out the 
  -- leading comma one you are done.
  select @platforms = @platforms + ', ' + Plataforma from dbo.Jogos 
        where JogoId = @jogoid;
  return @platforms;
end;
go;


SELECT 
    dbo.Jogos.NomeJogo AS Jogo, 
    dbo.getPlatforms(JogoId)
FROM dbo.Jogos 
GROUP BY NomeJogo, JogoId;

不幸的是,group_concat 在 SQL Server 中不可用(开箱即用),即连接不是聚合操作之一(与 SUM、COUNT、AVG 等不同)。

各种方法的比较,包括它们的执行方式,是here

【讨论】:

  • 感谢您的回复,我很容易理解这个概念以及您编写的函数如何工作,问题是我对使用函数和触发器之类的东西真的很陌生,因为我们的老师只教过我们四个基本命令,我真的不明白如何在此处添加一个 Join 以使其真正起作用。我明白,虽然我基本上需要为我想连接的每个“值”提供一个函数,对吧?
  • 是的,如果你遵循这条路线。我喜欢这种方式,因为它是“程序化的”,并且对于新手来说很容易理解,但同样,它并不是表现最好的选择。
【解决方案2】:

正如其他人所说,您可以使用 xml 路径对 concat 进行分组。但是,根据处理大型数据集的个人经验,我发现从查询中获取结果然后将它们连接到代码中要快得多。

例如,您可以这样创建一个对象:

public class Game
{
    public string Title { get; set; }
    public string Genre { get; set; }
    public string Platform { get; set; }
    public string Developer { get; set; }
}

然后,您可以使用 linq 以您认为合适的方式操作从查询返回的数据集。例如:

using(var command = new SqlCommand())
{
     /*init your sql command */
     var dataSet = new DataSet("Games");
     var dataAdapter = new SqlDataAdapter();
     /*set data adapter command (your query)*/
     dataAdapter.Fill(dataSet);

     var games = new List<Game>();

     var groupings = dataSet.Tables[0].Cast<DataRow>().GroupBy(x => x["Game"].ToString()); //Here we are grouping by games. Based on your example we'll have one group "Assassin's Creed". 

     foreach(var grouping in groupings)
     {
          var firstRow = grouping.First();

          var game = new Game()
          {
               Title = firstRow["Game"].ToString(),
               Genre = string.Join(",", grouping.GroupBy(x => x["Genre"]).Select(x => x.First()["Genre"].ToString()), //Grouping eliminates dupes. This should return Action, Stealth based on your example
               Platform = string.Join(",", grouping.GroupBy(x => x["Platform"]).Select(x => x.First()["Platform"].TosString()),
               Developer = firstRow["Developer"].ToString() //I'm assuming each game has exactly one developer
          }
          games.Add(game);
    }
    //The games list should now have exactly what you need. Based on your example it would have only looped once since there is only one game so you'll have a list with one game object
}

我不确定这是否对您有帮助,但我注意到这比在 sql server 中按 xml 路径进行分组要快得多。

【讨论】:

    【解决方案3】:

    这叫group_concat in sql server

    喜欢:

    CREATE TABLE #GAME ([NAME] varchar(50), [Genre] VARCHAR(50), [Platform] varchar(50),Developper VARCHAR(50))
    
    INSERT INTO #GAME  VALUES ('A','g1','ps1','User1'),('A','g2','ps2','User2'),('A','g3','ps1','User1')
    
    
    SELECT 
      [NAME],
      STUFF((SELECT ', ' + [Genre] FROM #GAME WHERE (NAME = Results.NAME) FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)'),1,2,'') AS GEN1,
      STUFF((SELECT ', ' + [Platform] FROM #GAME WHERE (NAME = Results.NAME) FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)'),1,2,'') AS Platform1,
      STUFF((SELECT ', ' + Developper FROM #GAME WHERE (NAME = Results.NAME) FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)'),1,2,'') AS Developper1
    
    FROM #GAME Results
    GROUP BY NAME
    

    编辑 2:

    CREATE TABLE #GAME ([NAME] varchar(50), [Genre] VARCHAR(50), [Platform] int,Developper VARCHAR(50))
    INSERT INTO #GAME  VALUES ('A','g1',1,'User1'),('A','g2',2,'User2'),('A','g3',3,'User1')
    
    Create Table #PLATFORM(ID int,NAME varchar(50),)
    INSERT INTO #PLATFORM  VALUES (1,'ps1'),(2,'ps2'),(3,'ps3')
    
    
    ;with cte as
    (select gm.Name as Name,pl.Name as plt
    from #GAME gm join #PLATFORM pl on gm.Platform=pl.ID
    )
    
    select Name,
    STUFF((SELECT ', ' + plt FROM cte WHERE (NAME = Results.NAME) FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)'),1,2,'') AS Platform1
    FROM cte Results
    GROUP BY NAME
    

    【讨论】:

    • 我知道这个是如何工作的,但我的问题是如何应用这个概念来获取与游戏相关但在不同表中的数据?因为例如我有游戏表和平台表,这是一个 N-N 关系。它仍然以这种方式工作吗?我有点困惑 |: 不幸的是,我的老师并没有教我们更多的选择、更新、插入、删除:S
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 2015-01-08
    • 2015-05-05
    • 1970-01-01
    • 2019-02-12
    相关资源
    最近更新 更多