青训营打卡Day2

122 阅读3分钟

一、【多选】下列关于Join 运算不正确的是:

a. Nested Loop Join 不能使用索引做优化。
b. 如果左表太大,不能放入内存中,则不能使用 Hash Join。
c. 如果 Join 的一个输入表在 Join Key 上有序,则一定会使用 Sort Merge Join。
d. Broadcast Join 适用于一张表很小,另一张表很大的场景。

a选项:Nested Loop Join可以使用索引优化。
b选项:可以先分区,再连接
c选项:还可以使用hash join

Sql Server支持三种物理连接:nested loops join(嵌套连接),merge join和hash join.

nested loops join(NLJ)

NLJ是通过两层循环,用第一张表做Outter Loop,第二张表做Inner Loop,Outter Loop的每一条记录跟Inner Loop的记录作比较,符合条件的就输出。 有三种算法:

  • Simple Nested Loop Join(SNLJ)算法

SNLJ就是两层循环全量扫描连接的两张表,得到符合条件的两条记录则输出,这也就是让两张表做笛卡尔积,比较次数是R * S,是比较暴力的算法,会比较耗时。

  • Index Nested Loop Join(INLJ)算法

INLJ是在SNLJ的基础上做了优化,通过连接条件确定可用的索引,在Inner Loop中扫描索引而不去扫描数据本身,从而提高Inner Loop的效率。 而INLJ也有缺点,就是如果扫描的索引是非聚簇索引,并且需要访问非索引的数据,会产生一个回表读取数据的操作,这就多了一次随机的I/O操作。

  • Block Nested Loop Join(BNLJ)算法

BNLJ在SNLJ的基础上使用了join buffer,会提前读取Inner Loop所需要的记录到buffer中,以提高Inner Loop的效率。

merge join

Merge join 合并连接。两个集合进行merge join,需要有一个等值的条件,然后需要两个已排序好的集合。

Merge join的操作通常分三步:
1. 对连接的每个表做table access full;
2. 对table access full的结果进行排序。
3. 进行merge join对排序结果进行合并。

在全表扫描比索引范围扫描再进行表访问更可取的情况下,Merge Join会比Nested Loop性能更佳。 Merge Join可适于于非等值Join(>,<,>=,<=,但是不包含!=,也即<>)

hash join

Hash Join是做大数据集连接时的常用方式,优化器使用两个表中较小(相对较小)的表利用Join Key在内存中建立散列表,然后扫描较大的表并探测散列表,找出与Hash表匹配的行。

Hash Join只能应用于等值连接

类别Nested LoopHash JoinMerge Join
使用条件任何条件等值连接(=)等值或非等值连接(>,<,=,>=,<=),‘<>’除外
相关资源CPU、磁盘I/O内存、临时空间内存、临时空间
特点当有高选择性索引或进行限制性搜索时效率比较高,能够快速返回第一次的搜索结果。当缺乏索引或者索引条件模糊时,Hash Join比Nested Loop有效。通常比Merge Join快。在数据仓库环境下,如果表的纪录数多,效率高。当缺乏索引或者索引条件模糊时,Merge Join比Nested Loop有效。非等值连接时,Merge Join比Hash Join更有效
缺点当索引丢失或者查询条件限制不够时,效率很低;当表的纪录数多时,效率低。为建立哈希表,需要大量内存。第一次的结果返回较慢。所有的表都需要排序。它为最优化的吞吐量而设计,并且在结果没有全部找到前不返回数据。

二、给定一个正整数数组 arrs 和整数 K ,请找出该数组内乘积小于等于 k 的连续的子数组的个数,算法时间复杂度o(n)。

java

class Solution {
    public int numSubarrayProductLessThanK(int[] arrs, int k) {
        // 特殊值判断
        if (k <= 1) return 0;
        
        // 定义所需变量
        int res = 0;
        int left = 0;
        int right = 0;
        int fac = 1;

        // 遍历
        while (right < arrs.length) {
            fac *= arrs[right];

            // 滑动
            while (left <= right && fac >= k) {
                fac /= arrs[left];
                left++;
            }
            res += right - left + 1;
            right++;
        }

        // 返回结果
        return res;
    }
}

go

func numSubarrayProductLessThanK(arrs []int, k int) int {
    //start: 10:28,... end 11.23
    //思路1: 滑动窗口
    //参数处理
    if len(arrs) == 0 || k <= 0 {
        return 0
    }

    //滑动窗口
    res := 0
    left,right,product := 0,0,1
    for ;right < len(arrs);right++ {
        product *= arrs[right]
        for left <= right && product >= k {
            product /= arrs[left]
            left ++
        }
        if left <= right {
            res += right-left +1
        }
    }
    return res
}