【问题标题】:Need some serious help with self join issue在自我加入问题上需要一些认真的帮助
【发布时间】:2010-06-15 14:22:26
【问题描述】:

您可能知道,您无法使用自连接索引视图。实际上,即使是同一张表的两个连接,即使它在技术上不是自连接。微软的几个人想出了一个解决方法。但是太复杂了,看不懂!!!

问题的解决方法在这里:http://jmkehayias.blogspot.com/2008/12/creating-indexed-view-with-self-join.html

我想将此工作应用于的视图是:

create VIEW vw_lookup_test
WITH SCHEMABINDING
AS
select
count_big(*) as [count_all],
awc_txt,
city_nm,
str_nm,
stru_no,
o.circt_cstdn_nm [owner],
t.circt_cstdn_nm [tech],
dvc.circt_nm,
data_orgtn_yr 
from 
((dbo.dvc 
join dbo.circt 
on dvc.circt_nm = circt.circt_nm) 
join dbo.circt_cstdn o
on circt.circt_cstdn_user_id = o.circt_cstdn_user_id)
join dbo.circt_cstdn t
on dvc.circt_cstdn_user_id = t.circt_cstdn_user_id
group by
awc_txt,
city_nm,
str_nm,
stru_no,
o.circt_cstdn_nm,
t.circt_cstdn_nm,
dvc.circt_nm,
data_orgtn_yr 
go

任何帮助将不胜感激!!!

提前非常感谢!

编辑:所以我发现这也可以。请注意,我在第一个索引视图中加入了一次表,第二次在第二个非索引视图中加入。

alter VIEW vw_lookup_owner_test2
WITH SCHEMABINDING  
AS 
select
count_big(*) as [countAll],
awc_txt,
city_nm,
str_nm,
stru_no,
dvc.circt_nm,
circt_cstdn_nm,
data_orgtn_yr,
dvc.circt_cstdn_user_id
from dbo.dvc 
join dbo.circt
on dvc.circt_nm = circt.circt_nm
join dbo.circt_cstdn o
on circt.circt_cstdn_user_id = o.circt_cstdn_user_id
group by
awc_txt,
city_nm,
str_nm,
stru_no,
dvc.circt_nm,
circt_cstdn_nm,
data_orgtn_yr,
dvc.circt_cstdn_user_id
go

CREATE UNIQUE CLUSTERED INDEX [idx_vw_lookup_owner2_test1] ON [dbo].[vw_lookup_owner_test2] 
(
    [awc_txt] ASC,
    [city_nm] ASC,
    [str_nm] ASC,
    [stru_no] ASC,
    [circt_nm] ASC,
    [circt_cstdn_nm] ASC,
    [data_orgtn_yr] ASC,
    [circt_cstdn_user_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

create view vw_lookup_dvc_loc
as
select
awc_txt,
city_nm,
str_nm,
stru_no,
circt_nm,
o.circt_cstdn_nm as [owner],
--o.circt_cstdn_user_id,
t.circt_cstdn_nm as tech,
data_orgtn_yr
from vw_lookup_owner_test2 o With (NOEXPAND)
join circt_cstdn t
on o.circt_cstdn_user_id = t.circt_cstdn_user_id
group by
awc_txt,
city_nm,
str_nm,
stru_no,
circt_nm,
o.circt_cstdn_nm,
data_orgtn_yr,
t.circt_cstdn_nm
--o.circt_cstdn_user_id

然后我可以根据需要在第一个视图上创建附加索引。我不确定这个解决方案(或解决这个问题的解决方法)是否真的会加快性能,但我会告诉你的。

【问题讨论】:

    标签: sql database tsql views self-join


    【解决方案1】:

    这是我从博文中得到的信息

    • 假设您想在 dbo.circt_cstdn 上加入 2 次,即您想要类似的东西

               owner       tech
      rowA     a.nm        b.nm
      ...
      
    • 不是将值放入 2 列,而是将其放入 2 行(上面每行 2 行)并添加一个额外的列来说明哪一行是哪一列。注意第 1.1 行和第 1.2 行的数据相同(名称和列除外)

               name   for
      row1.1   nm     owner
      row1.2   nm     tech
      ...
      
    • 然后您以所有者和技术人员的名称列的最大值为轴。注意 - max 函数只是为了欺骗 PIVOT(需要一些聚合函数),如果只有一条记录,您可以使用任何返回相同值的聚合函数 所有者技术 行 1 纳米 纳米 ...

    现在,如果我们为您的查询这样做

    1. 创建一个表d,像这个

       i
       1
       2
      
    2. 用这个交叉连接查询的第一部分

      SELECT 
           count_big(*) as [count_all], 
           awc_txt, 
           city_nm, 
           str_nm, 
           stru_no, 
           dvc.circt_nm, 
           data_orgtn_yr
      FROM  
           dbo.dvc  
           INNER JOIN dbo.circt on dvc.circt_nm = circt.circt_nm
           CROSS JOIN dbo.d  
      GROUP BY
           awc_txt, city_nm, str_nm, stru_no, dvc.circt_nm, data_orgtn_yr, d.i
      
    3. 现在让我们在 D.i 为 1 时使用所有者的行,如果 D.i 为 2 则使用技术

      SELECT 
           count_big(*) as [count_all], 
           awc_txt, 
           city_nm, 
           str_nm, 
           stru_no, 
           dvc.circt_nm, 
           data_orgtn_yr,
           Case 
               WHEN d.i = 1 THEN 'Owner'
               WHEN d.i = 2 THEN 'Tech'
           END
      FROM  
           dbo.dvc  
           INNER JOIN dbo.circt on dvc.circt_nm = circt.circt_nm
           CROSS JOIN dbo.d 
      GROUP BY
           awc_txt, city_nm, str_nm, stru_no, dvc.circt_nm, data_orgtn_yr, 
           Case 
               WHEN d.i = 1 THEN 'Owner'
               WHEN d.i = 2 THEN 'Tech'
           END  
      
    4. 现在添加 nm 列。要获得名称,如果它是所有者行 (d.i = 1),则使用 circt 加入 circt_cstdn,如果是技术行 (d.i = 2),则使用 dvc。注意 - 我在这里尝试了一个快捷方式,将它放在连接条件中。如果不行,试试博文的方式(在 circt.circt_cstdn_user_id OR dvc.circt_cstdn_user_id 上加入,然后使用 WHERE 子句过滤掉)

      SELECT 
           count_big(*) as [count_all], 
           awc_txt, 
           city_nm, 
           str_nm, 
           stru_no, 
           dvc.circt_nm, 
           data_orgtn_yr,
           Case 
               WHEN d.i = 1 THEN 'Owner'
               WHEN d.i = 2 THEN 'Tech'
           END as PersonType,
           circt_cstdn_nm
      FROM  
           dbo.dvc  
           INNER JOIN dbo.circt on dvc.circt_nm = circt.circt_nm
           CROSS JOIN dbo.d 
           INNER JOIN dbo.circt_cstdn on circt_cstdn_user_id = 
                CASE
                     WHEN d.i = 1 THEN circt.circt_cstdn_user_id
                     WHEN d.i = 2 THEN dvc.circt_cstdn_user_id
                END
      GROUP BY
           awc_txt, city_nm, str_nm, stru_no, dvc.circt_nm, data_orgtn_yr, 
           Case 
               WHEN d.i = 1 THEN 'Owner'
               WHEN d.i = 2 THEN 'Tech'
           END,
           circt_cstdn_nm
      
    5. 使用它创建一个视图并创建索引

      create VIEW vw_lookup_test_imed
      WITH SCHEMABINDING 
      AS
          <<query above>>  
      GO
      
      spell to create INDEX
      
    6. 现在您 PIVOT 将 PersonType 列转换为 Owner 和 Tech 列

      SELECT 
           count_all, 
           awc_txt, 
           city_nm, 
           str_nm, 
           stru_no, 
           dvc.circt_nm, 
           data_orgtn_yr,
           [Owner], 
           [Tech] 
      FROM 
      ( 
           SELECT 
                count_all, 
                awc_txt, 
                city_nm, 
                str_nm, 
                stru_no, 
                dvc.circt_nm, 
                data_orgtn_yr,
                PersonType,
                circt_cstdn_nm
           FROM dbo.vw_lookup_test_imed WITH (NOEXPAND) 
      ) src 
      PIVOT 
      ( 
           MAX(circt_cstdn_nm) 
           FOR PersonType IN ([Owner], [Tech]) 
      ) pvt 
      

    如果有语法错误(肯定有很多,因为我现在无法访问数据库)请告诉我。

    【讨论】:

    • 这最后一个查询是您发布的另一个视图吗?因为我的理解是我不能索引引用另一个视图的视图......或者我要索引第一个视图?并使用第二个...?
    • 好的。我重读了你的帖子,我很确定我会索引第一个视图。但是我将如何索引第一个视图?
    • 好的!我创建了第一个视图。创建了一个超过 8 列的唯一聚集索引,然后我从第二个视图运行 select 语句,我得到 300486 行而不是 299925,这是我运行原始语句时得到的行数。哈哈,你施展了魔法 :) 你创造了 561 行......有什么想法吗?
    • 哇!让我们检查几件事。 1. 新的 vw_lookup_test_imed 返回多少行 - 2 x 300486 或 2 x 299925。 2. 你能做一个 EXCEPT ,并检查是否有任何空值(或用户 ID 不在 circt_cstdn 中)那里?如果原始查询执行 INNER JOIN,则如果缺少 Owner 或 Tech id,则将排除行。我猜枢轴会返回一行,但为缺少的所有者或技术人员输入空白/空值。
    • 好的。 1. 所以查询返回 599222 行 (2 X 299611) 枢轴查询返回 300486 行。 (是的,除了语法修复之外,我完全按照原样复制并粘贴了您在上面写的内容)。 2. 所有的join都有FK约束,circt_cstdn_user_id, circt_cstdn_nm or circt_nm cols中没有空值。所有查询都使用连接关键字“join” no left,inner,outer.... 我必须说“join”才能索引视图,这就是我所做的。无论如何,左连接和连接返回相同数量的行...
    【解决方案2】:

    我认为这种 JOIN 语法很糟糕,起源于 MS Access。啊。 我建议你使用:

    select
    count_big(*) as [count_all],
    awc_txt,
    city_nm,
    str_nm,
    stru_no,
    o.circt_cstdn_nm [owner],
    t.circt_cstdn_nm [tech],
    dvc.circt_nm,
    data_orgtn_yr 
    
    -- HERE
    from dbo.dvc
    join dbo.circt on (dvc.circt_nm = circt.circt_nm) 
    join dbo.circt_cstdn o on (circt.circt_cstdn_user_id = o.circt_cstdn_user_id)
    join dbo.circt_cstdn t on (dvc.circt_cstdn_user_id = t.circt_cstdn_user_id)
    
    group by
    awc_txt,
    city_nm,
    str_nm,
    stru_no,
    o.circt_cstdn_nm,
    t.circt_cstdn_nm,
    dvc.circt_nm,
    data_orgtn_yr 
    

    这种语法更简洁、更易于理解,并且在 SQL Server、Firebird、Oracle、MySQL 和许多其他工具中得到认可。 现在您可以更好地看到“表”之间的关系。 当您两次或多次加入同一个“表”时,您需要为每个表添加别名。在一行中,“circt_cstdn”别名为“o”。另一方面,“circt_cstdn”别名为“t”。

    我建议使用 LEFT JOIN 或 INNER JOIN 代替 JOIN。

    【讨论】:

    • 两件事。首先我完全同意你的看法:我总是使用左连接。但是,您不能用左连接索引视图,我对其进行了测试,无论哪种方式我都得到相同的结果。其次,我认为您刚刚发布的内容与我在原始帖子中发布的内容几乎相同...我确实使用了别名 o 和 t...
    • 我很生气,你暗示我是从 MSAccess 中学到的数据库知识!我讨厌 MsAccess。 (使用的 sql 语言有愚蠢的限制并且很烂)。而“INNER JOIN”和“JOIN”完全一样……
    猜你喜欢
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多