oracle 在实体中使用没有主键的视图

5rgfhyps  于 2023-04-05  发布在  Oracle
关注(0)|答案(7)|浏览(176)

我刚开始一个项目,将应用程序从原始ADO.NET和嵌入式SQL转换为Entity。我遇到了应用程序使用的一个视图的问题。该视图没有主键,也没有唯一标识行的列(或列的组合)。下面是创建视图时使用的select:

SELECT
    filingmonth,
    CEIL(filingmonth / 3),
    licnum,
    filingyear,
    DECODE(GROUPING(insurername), '1', '- All Insured -', insurername),
    insurername,
    policylinecode,
    linedescription,
    SUM(NVL(grosspremium, 0)),
    SUM(DECODE(taxexempt, 1, grosspremium, 0)),
    TRUNC(
      CASE
        WHEN
          (
            b.rsn IS NOT NULL
            OR A.zeroreport = 1
          )
          AND b.datereceived IS NULL
            THEN A.datereceived
        ELSE b.datereceived
      END),
    SUM(aip.iscompanyadmitted(b.naiccocode, b.naicalienid)),
    A.insuredid
  FROM
    aip.slbtransinsured A
  LEFT OUTER JOIN aip.slbtransinsurer b
  ON
    A.insuredid = b.insuredid
  LEFT OUTER JOIN aip.slblinecodes C
  ON
    b.policylinecode = C.linecode
  WHERE
    A.submitted = 1
  AND A.entryincomplete = 0
  GROUP BY
    licnum,
    filingmonth,
    filingyear,
    TRUNC(
      CASE
        WHEN
          (
            b.rsn IS NOT NULL
            OR A.zeroreport = 1
          )
          AND b.datereceived IS NULL
            THEN A.datereceived
        ELSE b.datereceived
      END),
    ROLLUP(insurername, aip.iscompanyadmitted(b.naiccocode, b.naicalienid),
    policylinecode, linedescription), A.insuredid;

下面是一些示例数据,显示有一些行是完全重复的(第3行和第4行):

FILINGMONTH CEIL(FILINGMONTH/3) LICNUM FILINGYEAR DECODE(GROUPING(INSURERNAME),'1','-ALLINSURED-',INSURERNAME)                                         INSURERNAME                                                                                          POLICYLINECODE LINEDESCRIPTION                                                                                                                                                                                          SUM(NVL(GROSSPREMIUM,0)) SUM(DECODE(TAXEXEMPT,1,GROSSPREMIUM,0)) TRUNC(CASEWHEN(B.RSNISNOTNULLORA.ZEROREPORT=1)ANDB.DATERECEIVEDISNULLTHENA.DATERECEIVEDELSEB.DATERECEIVEDEND) SUM(AIP.ISCOMPANYADMITTED(B.NAICCOCODE,B.NAICALIENID)) INSUREDID

      6                   2   8150       2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                            SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                            17             OTHER LIABILITY                                                                                                                                                                                                            721.25                                       0 18-JUL-07                                                                                                                                                          0        81 
      6                   2   8150       2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                            SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                            17                                                                                                                                                                                                                                        721.25                                       0 18-JUL-07                                                                                                                                                          0        81 
      6                   2   8150       2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                            SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                                                                                                                                                                                                                                                                      721.25                                       0 18-JUL-07                                                                                                                                                          0        81 
      6                   2   8150       2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                            SAVERS PROPERTY AND CASUALTY INSURANCE CO                                                                                                                                                                                                                                                                                                      721.25                                       0 18-JUL-07                                                                                                                                                          0        81

insuredid是aip.slbtransinsured表的pk,rsn是aip.slbtransinsurer和aip.slblinecodes的pk。
是否有可能在没有唯一标识符的情况下向实体模型添加视图?或者是否有一种简单的方法向视图添加唯一的行标识符?视图只能从中读取,不能写入。

s1ag04yj

s1ag04yj1#

是否有可能在没有唯一标识符的情况下向实体模型添加视图?
如果没有主键,则为否。这将导致以下类型的error
在模型生成期间检测到一个或多个验证错误:
System.Data.Edm.EdmEntityType::EntityType“SalesOnEachCountry”未定义键。请为此EntityType定义键。System.Data.Edm.EdmEntitySet:实体类型:EntitySet SalesOnEachCountryList基于未定义键的类型SalesOnEachCountry。
如果没有唯一标识符,是的,尽管它有一个不想要的输出。具有相同标识符的记录将引用相同的对象,这称为Identity Map Pattern
举个例子,即使你的视图生成了这两行:

Country     Year TotalSales
Philippines 2010 20.000000
Philippines 2011 40.000000

如果您只将主键Map到Country字段,例如

public class SalesOnEachCountry
{        
    [Key]
    public int CountryId { get; set; }
    public string CountryName { get; set; }        
    public int OrYear { get; set; }
    public long SalesCount { get; set; }
    public decimal TotalSales { get; set; }
}

,即使您的视图在Oracle查询编辑器上生成上述两行,Entity Framework也会生成以下错误输出:

Country     Year TotalSales
Philippines 2010 20.000000
Philippines 2010 20.000000

Entity Framework会认为第二行和第一行是同一个对象。
为了保证唯一性,必须确定哪些列使每行唯一。在上面的示例中,必须包括Year,以便主键是唯一的。

public class SalesOnEachCountry
{        
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }

    public long SalesCount { get; set; }      
    public decimal TotalSales { get; set; }
}

使您的主键类似于上面的属性,实体框架可以正确地将每个视图的行Map到它们自己的对象。因此,实体框架现在可以显示与视图完全相同的行。

Country     Year TotalSales
Philippines 2010 20.000000
Philippines 2011 40.000000

完整详情如下:http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
然后,对于没有任何列来使行唯一的视图,要保证Entity Framework可以将视图的每一行Map到它们自己的对象,最简单的方法是为视图的 * 主键 * 创建一个单独的列,一个很好的选择是在每行上创建一个行号列。

create view RowNumberedView as

select 
    row_number() over(order by <columns of your view sorting>) as RN
    , *
from your_existing_view

然后在class RowNumberedView的RN属性上分配[Key]属性

qxgroojn

qxgroojn2#

以下是Michael Buen的回答:我发现用ISNULL()向视图添加行号将允许实体框架拉入视图并自动创建必要的EntitySet数据。

create view RowNumberedView as

select 
    ISNULL(ROW_NUMBER() OVER (ORDER BY <column>), 0) AS RN
    , *
from your_existing_view
uyto3xhc

uyto3xhc3#

在最近的工作中,我遇到了同样的问题。根据我的研究,我找不到任何关于如何在没有PK的情况下将视图附加到EF 6 CodeFirst的答案。大多数似乎涉及迁移并且非常混乱。我相信DB first对工作SQL VIEWS有更好的支持。
我确实尝试过引入一个window function(RowNumber),其想法是使用行标识符作为PK来保持EF 6满意。但这使我的查询总体上更昂贵,所以我不得不放弃这个想法。
最后,我不得不仔细分析我的数据集,看看我是否可以引入一个复合键-一个覆盖我的业务应用程序需要确保工作的所有场景的键。记住也使用IsNull(ColumnName,0),以确保您可以满足CodeFirst流畅方法中的.IsRequired()

HasKey(x => new { x.KfiId, x.ApplicationNumber, x.CustomerId });

我希望这对某些人有所帮助-对我来说,答案是分析视图反规范化的数据集并寻找复合键。
另一个很酷的想法,你可以尝试建议Marc Cals

aamkag61

aamkag614#

如果您在ASP.NET中使用实体框架和MVC

如前所述,创建具有自动递增或ROW_NUMBER列的视图。假设您有该列,其名称为rowNumber
然后转到MVC应用程序的Models目录中的上下文文件(yourDatabaseNameContext)文件,找到视图的定义,而不是

modelBuilder.Entity<yourView>(entity =>
    {
        entity.HasNoKey();

将其更改为:

modelBuilder.Entity<yourView>(entity =>
            {
                entity.HasKey(e => e.rowNumber);
wswtfjt7

wswtfjt75#

是否有可能在没有唯一标识符的情况下向实体模型添加视图?
有可能在视图中没有创建主键的单个列或一组列;因此,您最终会得到虚假的关系。2数据仓库表有时遵循这种形式。3简而言之,由于性能原因或报告原因,有时不遵循规范化。

第二点:

或者有没有一种简单的方法可以向视图中添加唯一的行标识符?
我建议你从slbtransinsured中选择所有列,看看是否能找到唯一标识每条记录的一列。在我看来,数据应该在slblinecodes中有一个你需要选择的代码类型,有点像查找。
为了好玩,试着运行这个并告诉我你得到了什么:

SELECT filingmonth,
       CEIL (filingmonth / 3),
       licnum,
       filingyear,
       DECODE (GROUPING (insurername), '1', '- All Insured -', insurername),
       insurername,
       policylinecode,
       linedescription,
       SUM (NVL (grosspremium, 0)),
       SUM (DECODE (taxexempt, 1, grosspremium, 0)),
       TRUNC (
           CASE
               WHEN (b.rsn IS NOT NULL OR a.zeroreport = 1)
                    AND b.datereceived IS NULL
               THEN
                   a.datereceived
               ELSE
                   b.datereceived
           END),
       SUM (aip.iscompanyadmitted (b.naiccocode, b.naicalienid)),
       a.insuredid
  FROM aip.slbtransinsured a
       LEFT OUTER JOIN aip.slbtransinsurer b
           ON a.insuredid = b.insuredid
       LEFT OUTER JOIN aip.slblinecodes c
           ON b.policylinecode = c.linecode
 WHERE a.submitted = 1 AND a.entryincomplete = 0
GROUP BY filingmonth,
         licnum,
         filingyear,
         DECODE (GROUPING (insurername), '1', '- All Insured -', insurername),
         insurername,
         policylinecode,
         linedescription,
         TRUNC (
             CASE
                 WHEN (b.rsn IS NOT NULL OR a.zeroreport = 1)
                      AND b.datereceived IS NULL
                 THEN
                     a.datereceived
                 ELSE
                     b.datereceived
             END),
         a.insuredid;
bkkx9g8r

bkkx9g8r6#

NEWID()ROW_NUMBER() OVER(...)都会生成唯一的值,就像其他答案中提到的那样,但是它们在视图结果中会显示为可空值(即使我们知道这些值永远不会为空)。这防止了它们在实体框架中被选为键。
修复方法是将它们 Package 在“ISNULL()”中,如下所示。

CREATE VIEW MyView
AS
SELECT
    ISNULL(NEWID(), 0x0) as RowId,
    ...
FROM ...

-- *** See performance note below ***
CREATE VIEW MyView
AS
SELECT
    ISNULL(ROW_NUMBER() OVER(ORDER BY (SELECT NULL)), 0) as RowNum,
    ...
FROM ...

结果RowIdRowNum列现在将在视图结果中报告为不可空类型。分配的值不可重复,只能用于只读用例。

重要说明:ROW_NUMBER()方法可能会引入不希望的性能问题。为了计算结果,SQL Server似乎会在应用引用查询可能定义的任何筛选器之前检索并将行号值分配给整个视图行集。例如,查询SELECT * FROM MyView V WHERE V.ActivityDate >= @StartDate AND V.ActivityDate < @EndDate可能会导致在应用日期范围筛选器之前检索所有时间的数据并分配行号。使用NEWID()时,这不是问题。

参见this db<>fiddle,显示各种形式。

du7egjpx

du7egjpx7#

在使用视图时考虑使用AsNoTracking()。这将禁用EF跟踪的任何关键字段。然后可以将任何非空字段手动定义为EF关键字(即使它重复)。
建议 * 不要 * 创建一个额外的行计数器字段,因为大多数行计数器最终要求引擎扫描视图的整个域以生成正确的计数器值,即使使用 predicate (where子句)进行查询。
请参见link

相关问题