【问题标题】:LINQ generates wrong sql query with system.data.sqliteLINQ 使用 system.data.sqlite 生成错误的 sql 查询
【发布时间】:2014-06-17 12:25:17
【问题描述】:

我有以下型号:

[Table(Name="word")]
public partial class Word
{
    [Column(IsPrimaryKey=true, Name="id")]
    public int Id { get; set; }

    [Column(IsPrimaryKey=true, Name="language_id")]
    public int LanguageId { get; set; }

    [Column(Name="translation")]
    public string Translation { get; set; }

    [Column(Name="category")]
    public string Category { get; set; }

    [Column(Name="order")]
    public int Order { get; set; }
}

还有如下代码:

var connection = new SQLiteConnection(@"Data Source=data.sqlite3");
var context = new DataContext(connection);
context.Log = Console.Out;

var test = from w1 in context.GetTable<Word> ()
           join w2 in context.GetTable<Word> () on w1.Id equals w2.Id
           select new {w1 = w1.Id, w2 =  w2.Id};

foreach (var i in test)
{
    Console.WriteLine("Word: {0} {1}", i.w1, i.w2);
}

输出如下所示:

SELECT w1$.[id], w2$.[id]
FROM [word] AS w1$, [word] AS w2$
WHERE (w1$.[id] = w1$.[id])
-- Context: SqlServer Model: AttributedMetaModel Build: 4.0.0.0
Word: 1 1
Word: 1 1
Word: 1 2
Word: 1 2
Word: 1 3
Word: 1 3
Word: 1 4
Word: 1 4
Word: 1 5
Word: 1 5

发生了什么事?为什么加入条件与我指定的不同?这是一个错误还是我做错了什么?值得一提的是,我在 Mac 上使用 Mono,并且按照他们网站上的描述为 Mono 编译了 system.data.sqlite。

更新:

这个变通办法解决了这个问题:

var test = from w1 in context.GetTable<Word> ()
           join w2 in context.GetTable<Word> () on w1.Id equals w2.Id
           where w1.Id == w2.Id
           select new {w1 = w1.Id, w2 =  w2.Id};

更新 2:

显然,linq 2 sql 与 sqlite 配合得非常糟糕。推荐的解决方案是使用实体框架。

【问题讨论】:

  • 你加入同一张表的同一列有什么原因吗?
  • 是的,这是设计使然。这个想法是我将不同语言的单词保存在“单词”表中。具有相同含义的单词具有相同的 id(但不同的 language_ids)。我想把一个词连同它的翻译一起来。问题中的查询是一个最小示例,它展示了我遇到的问题。如您所见,linq 将连接条件从w1.id = w2.id(在linq 中)更改为w1.id = w1.id(在sql 中)。显然,这没有意义。我不明白为什么会这样。

标签: c# sql linq sqlite mono


【解决方案1】:

我认为问题在于 SQLite 驱动程序无法很好地处理自连接。我只是在 SQLServer 中尝试了同样的事情

我的桌子是这样的

Id  Language_id Translation
1   1   hello
1   2   bonjour
1   3   yo
2   1   bye
2   2   aurevoiur
2   3   laterz

输出看起来像这样(我添加了翻译列以帮助查看发生的事情更容易)

SELECT [t0].[id] AS [w1], [t0].[translation] AS [word1], [t1].[id] AS [w2], 
[t1].[translation] AS [word2]
FROM [word] AS [t0]
INNER JOIN [word] AS [t1] ON [t0].[id] = [t1].[id]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.18408

Word: 1 = hello,  1 = hello
Word: 1 = bonjour,  1 = hello
Word: 1 = yo,  1 = hello
Word: 1 = hello,  1 = bonjour
Word: 1 = bonjour,  1 = bonjour
Word: 1 = yo,  1 = bonjour
Word: 1 = hello,  1 = yo
Word: 1 = bonjour,  1 = yo
Word: 1 = yo,  1 = yo
Word: 2 = bye,  2 = bye
Word: 2 = aurevoiur,  2 = bye
Word: 2 = laterz,  2 = bye
Word: 2 = bye,  2 = aurevoiur
Word: 2 = aurevoiur,  2 = aurevoiur
Word: 2 = laterz,  2 = aurevoiur
Word: 2 = bye,  2 = laterz
Word: 2 = aurevoiur,  2 = laterz
Word: 2 = laterz,  2 = laterz

它仍然没有给你你所追求的结果集(它给出了每个组合)-但至少它做了连接。

如果可能,我建议将您的数据分成两个表,使用 Word 表 - 带有 Id 和其他单列,并有一个翻译表 - 包含实际的翻译,以及返回到单词表的外键。我怀疑 sqlite 驱动程序会更好地处理这个问题。

【讨论】:

  • 感谢您使用另一个 sql 驱动程序对其进行测试!我想我会提交一个错误报告。您的结果集正是我所期望的。您现在可以添加一些 where 子句(如 w1.language_id = 1w2.language_id = 2),它们应该会过滤掉所有不需要的组合。
  • 另一种解决方法(我现在可能会使用它):添加连接条件作为附加的 where 子句。我在问题中添加了一个示例。
猜你喜欢
  • 2020-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-13
  • 1970-01-01
  • 2015-10-30
相关资源
最近更新 更多