GVKun编程网logo

SQL-如何在自引用表中获取给定值的顶级父项(sql 引用)

7

对于SQL-如何在自引用表中获取给定值的顶级父项感兴趣的读者,本文将会是一篇不错的选择,我们将详细介绍sql引用,并为您提供关于c#–实体框架在自引用表中遍历并返回子记录、c#–实体框架:如何在自引用

对于SQL-如何在自引用表中获取给定值的顶级父项感兴趣的读者,本文将会是一篇不错的选择,我们将详细介绍sql 引用,并为您提供关于c# – 实体框架在自引用表中遍历并返回子记录、c# – 实体框架:如何在自引用父子关系上指定外键列的名称?、lua – 如何在初始化期间自引用表、MsSQL如何在一个查询中获取给定日期和仓库编号的最接近的未来价值和价值总和的有用信息。

本文目录一览:

SQL-如何在自引用表中获取给定值的顶级父项(sql 引用)

SQL-如何在自引用表中获取给定值的顶级父项(sql 引用)

我有这个小问题。

该表基本上如下所示

学科

  • SubjectId
  • SubjectName
  • ParentSubjectId

ParentSubjectId引用主题表本身。并且可以下降很多级别(没有特定数量的级别)

示例(仅出于示例目的,使用国家/地区):

  1.Europe
  2.America
  1.1 France
  1.1 Italy
  2.1 USA
  2.2 Canada
  1.1.1 Paris
  1.2.1 Rome

等等..

SubjectID是GUID ParentSubjectID也是GUID。

样本概述:http :
//i.imgur.com/a2u2CfT.png

它甚至可以无限期降低水平(甚至可以降低到街道号​​的水平)

我的问题是:给定一个主题(无论深度如何)。我想获得该学科的顶级家长(Europe/America在这种情况下)

我怎样才能做到这一点 ?是否可以使用基本SQL查询?

请注意,我根本无法修改数据库(我正在从现有数据库中查询数据)

c# – 实体框架在自引用表中遍历并返回子记录

c# – 实体框架在自引用表中遍历并返回子记录

我正在使用Entity Framework并拥有一个BusinessUnits表,它可以引用同一类型的另一个记录来形成一个子级别.

我还有一组用户和用户权限,其中在此表中定义的每个用户都可以访问BusinessUnit和层次结构中的所有子业务单元.用户不应该访问所引用的BusinessUnit(如果存在).

如何处理这个自引用关系树并返回用户可以访问的所有业务单元(包含子单元)?是否可以在一个查询中执行此操作,还是需要自行手动构建一个循环的树?

我以这种方式从节点到父节点看到了模式的引用,这是否意味着我必须从最远的子节点开始,一次由一个父项构建树?

提前致谢,

克里斯

class BusinessUnit
{
    int BusinessUnitID {get;set;}
    public string BusinessName {get;set;}
    BusinessUnit ParentBusinessUnit {get;set;}
}

class User
{
    int UserID {get;set;}
    string Firstname {get;set;}
}

class UserPermissions
{
    [Key,ForeignKey("BusinessUnit"),Column(Order = 0)] 
    BusinessUnit BusinessUnit {get;set;}
    [Key,ForeignKey("User"),Column(Order = 1)] 
    User User {get;set;}
}

IEnumerable<BusinessUnit> GetUnitsForWhichUserHasAccess(User user)
{
/* Example 1
 given: BusinessUnitA (ID 1) -> BusinessUnitB (ID 2) -> BusinessUnitC (ID 3)
 with user with ID 1:
 and UserPermissions with an entry: BusinessUnit(2),User(1)
 the list { BusinessUnitB,BusinessUnitC } should be returned
*/

/* Example 2
 given: BusinessUnitA (ID 1) -> BusinessUnitB (ID 2) -> BusinessUnitC (ID 3)
 with user with ID 1:
 and UserPermissions with an entry: BusinessUnit(1),User(1)
 the list { BusinessUnitA,BusinessUnitB,BusinessUnitC } should be returned
*/
}

解决方法

好的,这里有几件事情.我们可以通过向模型添加更多属性来使其更容易.这是一个选择吗?如果是,请向实体添加集合属性.现在,我不知道你使用的是哪个EF API:DbContext(code first或edmx)或者ObjectContext.在我的示例中,我使用了具有edmx模型的DbContext API来生成这些类.

如果您愿意,使用几个注释,您可以省去edmx文件.

public partial class BusinessUnit
{
    public BusinessUnit()
    {
        this.ChlidBusinessUnits = new HashSet<BusinessUnit>();
        this.UserPermissions = new HashSet<UserPermissions>();
    }

    public int BusinessUnitID { get; set; }
    public string BusinessName { get; set; }
    public int ParentBusinessUnitID { get; set; }

    public virtual ICollection<BusinessUnit> ChlidBusinessUnits { get; set; }
    public virtual BusinessUnit ParentBusinessUnit { get; set; }
    public virtual ICollection<UserPermissions> UserPermissions { get; set; }
}

public partial class User
{
    public User()
    {
        this.UserPermissions = new HashSet<UserPermissions>();
    }

    public int UserID { get; set; }
    public string FirstName { get; set; }

    public virtual ICollection<UserPermissions> UserPermissions { get; set; }
}

public partial class UserPermissions
{
    public int UserPermissionsID { get; set; }
    public int BusinessUnitID { get; set; }
    public int UserID { get; set; }

    public virtual BusinessUnit BusinessUnit { get; set; }
    public virtual User User { get; set; }
}

public partial class BusinessModelContainer : DbContext
{
    public BusinessModelContainer()
        : base("name=BusinessModelContainer")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public DbSet<BusinessUnit> BusinessUnits { get; set; }
    public DbSet<User> Users { get; set; }
    public DbSet<UserPermissions> UserPermissions { get; set; }
}

@Chase大奖章是正确的,因为我们无法编写递归LINQ(甚至实体sql)查询.

选项1:Lazy Loading

启用延迟加载后,您可以执行此操作…

private static IEnumerable<BusinessUnit> UnitsForUser(BusinessModelContainer container,User user)
    {
        var distinctTopLevelBusinessUnits = (from u in container.BusinessUnits
                                             where u.UserPermissions.Any(p => p.UserID == user.UserID)
                                             select u).distinct().ToList();

        List<BusinessUnit> allBusinessUnits = new List<BusinessUnit>();

        foreach (BusinessUnit bu in distinctTopLevelBusinessUnits)
        {
            allBusinessUnits.Add(bu);
            allBusinessUnits.AddRange(GetChildren(container,bu));
        }

        return (from bu in allBusinessUnits
                group bu by bu.BusinessUnitID into d
                select d.First()).ToList();
    }

    private static IEnumerable<BusinessUnit> GetChildren(BusinessModelContainer container,BusinessUnit unit)
    {
        var eligibleChildren = (from u in unit.ChlidBusinessUnits
                                select u).distinct().ToList();

        foreach (BusinessUnit child in eligibleChildren)
        {
            yield return child;

            foreach (BusinessUnit grandchild in child.ChlidBusinessUnits)
            {
                yield return grandchild;
            }
        }
    }

选项2:预加载实体

但是,有一些方法可以优化此操作,以避免重复访问服务器.如果数据库中只有少量的业务单位,您可以加载整个列表.然后,由于EFs能够自动修复关系,只需加载用户和他的数据库权限就可以让我们所有的需要.

要澄清:这个方法意味着你加载所有的BusinessUnit实体;即使是用户没有权限的那些.但是,由于它大大减少了sql Server的“喋喋不休”,所以它仍然可以比上面的选项1更好.与下面的选项3不同,这是“纯”EF,而不依赖于特定的提供商.

using (BusinessModelContainer bm = new BusinessModelContainer())
        {
            List<BusinessUnit> allBusinessUnits = bm.BusinessUnits.ToList();

            var userWithPermissions = (from u in bm.Users.Include("UserPermissions")
                                       where u.UserID == 1234
                                       select u).Single();

            List<BusinessUnit> unitsForUser = new List<BusinessUnit>();

            var explicitlyPermittedUnits = from p in userWithPermissions.UserPermissions
                                           select p.BusinessUnit;

            foreach (var bu in explicitlyPermittedUnits)
            {
                unitsForUser.Add(bu);
                unitsForUser.AddRange(GetChildren(bm,bu));
            }

            var distinctUnitsForUser = (from bu in unitsForUser
                                        group bu by bu.BusinessUnitID into q
                                        select q.First()).ToList();
        }

请注意,上述两个例子可以改进,但可以作为一个例子让你走.

选项3:使用通用表表达式定制SQL查询

如果您有大量业务单位,您可能需要尝试最有效的方法.那就是执行使用层次结构公用表表达式的自定义sql来获取一次命中的信息.这当然会将实现与一个提供商(可能是sql Server)相结合.

你的sql将是这样的:

WITH UserBusinessUnits
            (BusinessUnitID,BusinessName,ParentBusinessUnitID)
            AS
            (SELECT Bu.BusinessUnitId,Bu.BusinessName,CAST(NULL AS integer)
                    FROM Users U
                    INNER JOIN UserPermissions P ON P.UserID = U.UserID
                    INNER JOIN BusinessUnits Bu ON Bu.BusinessUnitId = P.BusinessUnitId
                    WHERE U.UserId = ?
            UNION ALL
            SELECT  Bu.BusinessUnitId,Bu.ParentBusinessUnitId
                    FROM UserBusinessUnits Uu
                    INNER JOIN BusinessUnits Bu ON Bu.ParentBusinessUnitID = Uu.BusinessUnitId)
    SELECT  disTINCT
            BusinessUnitID,ParentBusinessUnitID
            FROM UserBusinessUnits

您将使用以下代码实现用户具有权限的BusinessUnit对象集合.

bm.BusinessUnits.sqlQuery(MysqLString,userId);

上述行与@Jeffrey建议的非常相似的代码之间存在细微差别.上述使用DbSet.SqlQuery(),而他的使用Database.SqlQuery.后者产生不被上下文跟踪的实体,而前者返回(默认情况下)跟踪的实体.跟踪的实体使您能够创建和保存更改,并自动修复导航属性.如果您不需要这些功能,请禁用更改跟踪(使用.AsNoTracking()或使用Database.SqlQuery).

概要

没有什么比实际的数据集测试,以确定哪种方法是最有效的.使用手工制作的sql代码(选项3)总是可能性能最好,但代价是使用更复杂的代码不太可移植(因为它与底层数据库技术相关).

请注意,您可以使用的选项取决于您使用的EF的“风味”,当然还有您选择的数据库平台.如果您想了解更多的具体指导,请使用额外的信息更新您的问题.

>你使用什么数据库?>您是否使用EDMX文件或首先编写代码?>如果使用EDMX,是否使用默认(EntityObject)代码生成技术或T4模板?

c# – 实体框架:如何在自引用父子关系上指定外键列的名称?

c# – 实体框架:如何在自引用父子关系上指定外键列的名称?

我正在尝试指定列名以将“外键”映射到使用Fluent API.我正在连接到sql Express的一个实例.我搜索过Stack Overflow和Google,但许多不同的配置示例给了我相同的结果.

产品类别

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
    public virtual Product ParentProduct { get; set; }
    public virtual ICollection<Product> ChildProducts { get; set; }
}

产品映射到实体框架

public class ProductMap : EntityTypeConfiguration<Product>
{
    public ProductMap() {
        HasKey(p => p.ProductId);

        Property(p => p.Name)
            .Isrequired();

        // Self referencing foreign key association 
        Property(c => c.ParentId)
            .IsOptional();

    HasMany(c => c.ChildProducts)
        .WithOptional(c => c.ParentProduct)
        .HasForeignKey(c => c.ParentId);
    }
}

问题

当我运行程序并且EF创建数据库时,结果是ParentID列出现NULL并且它创建了一个名为ParentProduct_ProductId的列.此列包含ParentId的正确值.

我不熟悉使用Fluent API和EF,所以我将这个问题归结为缺乏经验.如何让自动生成的列填充ParentId列?

解决方法

尝试此解决方案:

public class ProductMap : EntityTypeConfiguration<Product>
{
    public ProductMap() {
        HasKey(p => p.ProductId);

        Property(p => p.Name)
            .Isrequired();

        // Self referencing foreign key association 
        Property(c => c.ParentId)
            .IsOptional();

        HasOptional(x => x.Parent)
            .WithMany(x => x.ChildProducts)
            .HasForeignKey(x => x.ParentId)
            .WillCascadeOnDelete(false);
    }
}

lua – 如何在初始化期间自引用表

lua – 如何在初始化期间自引用表

有没有更短的方法来做到这一点:
local thisismytable = {
    non = sequitur
}
thisismytable.whatismytable = thisismytable

任何帮助,将不胜感激.
我不想重新创建预先存在的功能.

解决方法

没有.

如果你能够站在这两个表达式之间的区别这个主题:whatismytable()而不是thisismytable.whatismytable,你可以这样做:

local thisismytable = {
    non = sequitur,whatismytable = function (self) return self end
}

测试:

print(thisismytable)
print(thisismytable:whatismytable())

更多用法:

print(thisismytable:whatismytable().non)

MsSQL如何在一个查询中获取给定日期和仓库编号的最接近的未来价值和价值总和

MsSQL如何在一个查询中获取给定日期和仓库编号的最接近的未来价值和价值总和

您可以在过滤后使用窗口函数和条件聚合:

select warehouse,sum(value),max(case when seqnum = 1 then value end) as nearestfuturevalue,min(date) as nearestfuturedate
from (select t.*,row_number() over (partition by warehouse order by date) as seqnum
      from t
      where date > getdate()
     ) t
group by warehouse
,

您只能使用窗口功能:

select *
from (
    select 
        warehouse,sum(value) over(partition by warehouse) sum_future_value
        value nearest_future_value,date  nearest_future_date,row_number() over(partition by warehouse order by date) rn
    from mytable
    where date > getdate()
) t
where rn = 1

在子查询中,我们过滤将来的日期,按仓库计算即将到来的值的总和,并对记录进行排名。剩下要做的就是对每个组的更早记录进行过滤。

今天关于SQL-如何在自引用表中获取给定值的顶级父项sql 引用的介绍到此结束,谢谢您的阅读,有关c# – 实体框架在自引用表中遍历并返回子记录、c# – 实体框架:如何在自引用父子关系上指定外键列的名称?、lua – 如何在初始化期间自引用表、MsSQL如何在一个查询中获取给定日期和仓库编号的最接近的未来价值和价值总和等更多相关知识的信息可以在本站进行查询。

本文标签: