没有结果时的 LINQ to SQL 连接

发布于 2024-08-27 07:00:38 字数 819 浏览 8 评论 0原文

给定以下数据库结构 替代文本 http://dl.dropbox.com/u/26791/tables.png< /a>

我正在尝试编写一个 LINQ 查询,该查询将返回按与其关联的标签分组的图像。到目前为止,我已经得到了这个:

var images = from img in db.Images
    join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
    join t in db.Tags on imgTags.idTag equals t.idTag
    where img.OCRData.Contains(searchText.Text)
    group img by new { t.TagName } into aGroup
    select new
    {
        GroupName = aGroup.Key.TagName,
        Items = from x in aGroup
        select new ImageFragment()
        {
             ImageID = x.idImage,
             ScanDate = x.ScanTime
        }
    };

效果很好。但是,我还想返回一组“(未标记)”或其他内容中没有任何与之关联的标签的图像。如果不为每个图像插入默认标签,我无法理解如何做到这一点,这似乎通常不是一个很好的解决方案。

Given the following database structure
alt text http://dl.dropbox.com/u/26791/tables.png

I'm trying to write a LINQ query that will return images grouped by tags it's associated with. So far I've got this:

var images = from img in db.Images
    join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
    join t in db.Tags on imgTags.idTag equals t.idTag
    where img.OCRData.Contains(searchText.Text)
    group img by new { t.TagName } into aGroup
    select new
    {
        GroupName = aGroup.Key.TagName,
        Items = from x in aGroup
        select new ImageFragment()
        {
             ImageID = x.idImage,
             ScanDate = x.ScanTime
        }
    };

Which works great. However, I also want to return Images that do not have any tags associated with them in a group of "(Untagged)" or something. I can't wrap my head around how I would do this without inserting a default tag for every image and that seems like generally not a very good solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

二手情话 2024-09-03 07:00:38

如果在没有对应标签记录的情况下想要图像记录,则需要执行
图像标签表上的外部联接

If you want image records when there are no corresponding tag records, you need to perform an
outer join on the image tags table.

风轻花落早 2024-09-03 07:00:38

这有点棘手,但如果您能够实例化新的 ImageTagTag 实例以供 linq 使用,您可以在一个大型查询中完成此操作。本质上,当您进行外连接时,必须将 into 关键字与 DefaultIfEmpty(...) 方法结合使用来处理“外连接间隙” (例如,当典型的 SQL 左外连接中连接键的右侧为空时)。

var images = from img in db.Images
     join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
     into outerImageRef 
     from outerIR in outerImageRef.DefaultIfEmpty(new ImageTag() { idImage = img.idImage, idTag = -1 })
     join t in db.Tags on imgTags.idTag equals t.idTag
     into outerRefTags 
     from outerRT in outerRefTags.DefaultIfEmpty(new Tag(){ idTag=-1, TagName ="untagged"})
     group img by outerRT.TagName into aGroup
     select new {
         GroupName = aGroup.Key,
         Items = from x in aGroup
             select new ImageFragment() {
                 ImageID = x.idImage,
                 ScanDate = x.ScanTime
             }
     };

希望上面的内容能够编译,因为我没有您的确切环境,我使用自己的数据类型构建了我的解决方案,然后将其转换为您的问题的描述。基本上,关键部分是额外的 intoDefaultIfEmpty 行,它们本质上有助于将额外的“行”添加到内存中的大规模连接表中(如果您在考虑)传统的sql意义。

但是,有一种更具可读性的解决方案,不需要在内存中实例化 linq 实体(您必须自己将其转换为您的环境):

//this first query will return a collection of anonymous types with TagName and ImageId,
// essentially a relation from joining your ImageTags x-ref table and Tags so that
// each row is the tag and image id (as Robert Harvey mentioned in his comment to your Q)
var tagNamesWithImageIds = from tag in Tags
       join refer in ImageTags on tag.IdTag equals refer.IdTag
       select new {
           TagName = tag.Name,
           ImageId = refer.IdImage
       };
//Now we can get your solution by outer joining the images to the above relation
// and filling in the "outer join gaps" with the anonymous type again of "untagged"
// and then joining that with the Images table one last time to get your grouping and projection.
var images = from img in Images
     join t in tagNamesWithImageIds on img.IdImage equals t.ImageId
     into outerJoin
     from o in outerJoin.DefaultIfEmpty(new { TagName = "untagged", ImageId = img.IdImage })
     join img2 in Images on o.ImageId equals img2.IdImage
     group img2 by o.TagName into aGroup
     select new {
         TagName = aGroup.Key,
         Images = aGroup.Select(i => i.Data).ToList() //you'll definitely need to replace this with your code's logic. I just had a much simpler data type in my workspace.
     };

希望这是有道理的。
当然,您始终可以将应用程序设置为默认使用“未标记”来标记所有内容,或者执行一些更简单的 LINQ 查询来创建 ImageTag 表中不存在的图像 ID 列表,然后进行联合或其他操作。

It's a little tricky, but you can do it in one big query if you have the ability to instantiate new ImageTag and Tag instances for linq to work with. Essentially, when you're doing an outer join, you have to use the into keyword with the DefaultIfEmpty(...) method to deal with the "outer join gaps" (e.g., when the right side of the joined key is null in a typical SQL left outer join).

var images = from img in db.Images
     join imgTags in db.ImageTags on img.idImage equals imgTags.idImage
     into outerImageRef 
     from outerIR in outerImageRef.DefaultIfEmpty(new ImageTag() { idImage = img.idImage, idTag = -1 })
     join t in db.Tags on imgTags.idTag equals t.idTag
     into outerRefTags 
     from outerRT in outerRefTags.DefaultIfEmpty(new Tag(){ idTag=-1, TagName ="untagged"})
     group img by outerRT.TagName into aGroup
     select new {
         GroupName = aGroup.Key,
         Items = from x in aGroup
             select new ImageFragment() {
                 ImageID = x.idImage,
                 ScanDate = x.ScanTime
             }
     };

Hopefully the above compiles since I don't have your exact environment, I built my solution using my own data types and then converted it to your question's description. Basically the key parts are the extra into and DefaultIfEmpty lines that essentially help add the extra "rows" into the massively joined table that's in memory if you're thinking about it in the traditional sql sense.

However, there's a more readable solution that doesn't require the in memory instantiation of linq entities (you'll have to convert this one yourself to your environment):

//this first query will return a collection of anonymous types with TagName and ImageId,
// essentially a relation from joining your ImageTags x-ref table and Tags so that
// each row is the tag and image id (as Robert Harvey mentioned in his comment to your Q)
var tagNamesWithImageIds = from tag in Tags
       join refer in ImageTags on tag.IdTag equals refer.IdTag
       select new {
           TagName = tag.Name,
           ImageId = refer.IdImage
       };
//Now we can get your solution by outer joining the images to the above relation
// and filling in the "outer join gaps" with the anonymous type again of "untagged"
// and then joining that with the Images table one last time to get your grouping and projection.
var images = from img in Images
     join t in tagNamesWithImageIds on img.IdImage equals t.ImageId
     into outerJoin
     from o in outerJoin.DefaultIfEmpty(new { TagName = "untagged", ImageId = img.IdImage })
     join img2 in Images on o.ImageId equals img2.IdImage
     group img2 by o.TagName into aGroup
     select new {
         TagName = aGroup.Key,
         Images = aGroup.Select(i => i.Data).ToList() //you'll definitely need to replace this with your code's logic. I just had a much simpler data type in my workspace.
     };

Hope that makes sense.
Of course, you can always just set your application to tag everything by default w/ "untagged" or do some much simpler LINQ queries to create a list of image id's that are not present in your ImageTag table, and then union or something.

半枫 2024-09-03 07:00:38

这就是我最终所做的。我还没有真正检查过这会生成什么样的 SQL,我猜它可能不太漂亮。我认为我最好自己做几个查询并汇总这些内容,但无论如何这都是有效的:

var images = from img in db.Images
                     join imgTags in db.ImageTags on img.idImage equals imgTags.idImage into g
                     from imgTags in g.DefaultIfEmpty()
                     join t in db.Tags on imgTags.idTag equals t.idTag into g1
                     from t in g1.DefaultIfEmpty()
                     where img.OCRData.Contains(searchText.Text)
                     group img by t == null ? "(No Tags)" : t.TagName into aGroup
                     select new
                    {
                        GroupName = aGroup.Key,
                        Items = from x in aGroup
                                        select new ImageFragment()
                                        {
                                            ImageID = x.idImage,
                                            ScanDate = x.ScanTime
                                        }
                    };

Here's what I ended up doing. I haven't actually checked what kind of SQL this is generating yet, I'm guessing that it's probably not exactly pretty. I think I'd be better off doing a couple queries and aggregating the stuff myself, but in any case this works:

var images = from img in db.Images
                     join imgTags in db.ImageTags on img.idImage equals imgTags.idImage into g
                     from imgTags in g.DefaultIfEmpty()
                     join t in db.Tags on imgTags.idTag equals t.idTag into g1
                     from t in g1.DefaultIfEmpty()
                     where img.OCRData.Contains(searchText.Text)
                     group img by t == null ? "(No Tags)" : t.TagName into aGroup
                     select new
                    {
                        GroupName = aGroup.Key,
                        Items = from x in aGroup
                                        select new ImageFragment()
                                        {
                                            ImageID = x.idImage,
                                            ScanDate = x.ScanTime
                                        }
                    };
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文