你认为不可能的10个SQL技巧

以下10个技巧展示了仅用几行声明性SQL编写的惊人功能,从而产生简单且复杂的输出。

1.一切都是一张桌子

这是最琐碎的技巧,甚至不是真正的技巧,但这是全面理解SQL的基础:一切都是表格!当您看到这样的SQL语句时:
SELECT *
FROM person
…你会很快发现表person中坐在那里FROM条款。太酷了,那是一张桌子。但是您是否意识到整个语句也是一个表?例如,您可以编写:
SELECT *
FROM (
SELECT *
FROM person
) t
现在,您已经创建了所谓的“派生表” –即子句中的嵌套SELECT语句FROM。

2.使用递归SQL生成数据

公用表表达式(也称为:CTE,在Oracle中也称为子查询因式分解)是在SQL中声明变量的唯一方法(除了WINDOW只有PostgreSQL和Sybase SQL Anywhere才知道的晦涩的子句)。
这是一个强大的概念。极其强大。考虑以下语句:
– Table variables
WITH
t1(v1, v2) AS (SELECT 1, 2),
t2(w1, w2) AS (
SELECT v1 * 2, v2 * 2
FROM t1
)
SELECT *
FROM t1, t2
它产生
v1 v2 w1 w2
1 2 2 4
使用simple WITH子句,您可以指定表变量的列表(请记住:所有内容都是一个表),它们甚至可能相互依赖。

3.运行总计计算

从概念上讲,运行总计很容易理解。
在Microsoft Excel中,您只需计算两个先前(或后续)值的和(或差),然后使用有用的十字光标将该公式拉入整个电子表格。您通过电子表格“运行”总计。“总计”。
窗口函数是一个强大的概念–起初并不容易理解,但实际上,它们确实非常简单:
窗口函数是相对于由SELECT转换的当前行的行的子集的聚合/排名
而已。????
本质上,这意味着窗口函数可以对当前行“上方”或“下方”的行执行计算。与普通的和不同GROUP BY,它们不会转换行,这使它们非常有用。
语法可以总结如下,其中各个部分是可选的
1个
2
3
4
5 function(…) OVER (
PARTITION BY …
ORDER BY …
ROWS BETWEEN … AND …
)
因此,我们有任何类型的函数(稍后将看到此类函数的示例),后跟此OVER()子句,该子句指定了窗口。即该OVER()子句定义:
本PARTITION:是的,只有行相同的分区作为当前行会被认为是窗口
该ORDER:该窗口可以订购独立的正是我们选择
的ROWS(或RANGE)帧定义:窗口可以限制到行“超前”的固定量和“后面”

4.找到最大的无间隙系列

当我们进行声明式编程时,我们不再关心维护任何状态计数器和内存中计数器。我们想以在线分析SQL的形式表达这一点。即考虑以下数据:

LOGIN_TIME
2014-03-18 05:37:13
2014-03-16 08:31:47
2014-03-16 06:11:17
2014-03-16 05:59:33
2014-03-15 11:17:28
2014-03-15 10:00:11
2014-03-15 07:45:27
2014-03-15 07:42:19
2014-03-14 09:38:12

那没有太大帮助。让我们从时间戳中删除小时。这很容易:
1个
2
3
4 SELECT DISTINCT
cast(login_time AS DATE) AS login_date
FROM logins
WHERE user_id = :user_id
产生:

LOGIN_DATE
2014-03-18
2014-03-16
2014-03-15
2014-03-14

现在,我们已经了解了窗口函数,让我们向每个日期添加一个简单的行号:
1个
2
3
4 SELECT
login_date,
row_number() OVER (ORDER BY login_date)
FROM login_dates
产生:
| LOGIN_DATE | RN |
| ------------ || ---- |
| 2014-03-18 | 4 |
| 2014-03-16 | 3 |
| 2014-03-15 | 2 |
| 2014-03-14 | 1 |
还是容易。现在,如果不是将这些值分开选择,而是减去它们,会发生什么呢?
1个
2
3
4 SELECT
login_date -
row_number() OVER (ORDER BY login_date)
FROM login_dates
我们得到这样的东西:
| LOGIN_DATE | RN | GRP |
| ------------ || ---- | ------------ |
| 2014-03-18 | 4 | 2014-03-14 |
| 2014-03-16 | 3 | 2014-03-13 |
| 2014-03-15 | 2 | 2014-03-13 |
| 2014-03-14 | 1 | 2014-03-13 |

5.查找系列的长度

以前,我们已经看到了一系列连续的值。这很容易处理,因为我们可以滥用整数的连续性。如果“系列”的定义不太直观,又有几个系列包含相同的值,该怎么办?考虑以下数据,其中LENGTH我们要计算的每个序列的长度是:

ID VALUE_DATE 金额 长度
9997 2014-03-18 99.17 2
9981 2014-03-16 71.44 2
9979 2014-03-16 -94.60 3
9977 2014-03-16 -6.96 3
9971 2014-03-15 -65.95 3
9964 2014-03-15 15.13 2
9962 2014-03-15 17.47 2
9960 2014-03-15 -3.55 1
9959 2014-03-14 32.00 1

是的,你猜对了。连续(按ID排序)行具有相同的SIGN(AMOUNT)的事实定义了“系列”。再次检查格式如下的数据:

ID VALUE_DATE 金额 长度
9997 2014-03-18 +99.17 2
9981 2014-03-16 +71.44 2
9979 2014-03-16 -94.60 3
9977 2014-03-16 -6.96 3
9971 2014-03-15 -65.95 3
9964 2014-03-15 +15.13 2
9962 2014-03-15 +17.47 2
9960 2014-03-15 -3.55 1
9959 2014-03-14 +32.00 1

我们该怎么做呢?“简单”;)首先,让我们消除所有杂音,然后添加另一个行号:
1个
2
3
4
5
6 SELECT
id, amount,
sign(amount) AS sign,
row_number()
OVER (ORDER BY id DESC) AS rn
FROM trx
这将给我们:

ID 金额 招牌 RN
9997 99.17 1 1
9981 71.44 1 2
9979 -94.60 -1 3
9977 -6.96 -1 4
9971 -65.95 -1 5
9964 15.13 1 6
9962 17.47 1 7
9960 -3.55 -1 8
9959 32.00 1 9

现在,下一个目标是生成下表:

ID 金额 招牌 RN LO HI
9997 99.17 1 1 1
9981 71.44 1 2 2
9979 -94.60 -1 3 3
9977 -6.96 -1 4
9971 -65.95 -1 5 5
9964 15.13 1 6 6
9962 17.47 1 7 7
9960 -3.55 -1 8 8 8
9959 32.00 1 9 9 9

6. SQL的子集总和问题

本质上,对于所有这些总数……

ID 总计
1 25150
2 19800
3 27511

…我们希望找到可能的“最佳”(即最接近)总和,包括以下各项的任意组合:

ID 项目
1 7120
2 8150
3 8255
4 9051
5 1220
6 12515
7 13555
8 5221
9 812
10 6562

由于您很快就可以进行脑力数学运算,因此您立即计算出这是最好的总和:

总计 最佳 计算方式
25150 25133
19800 19768
27511 27488

如何使用SQL?简单。只需创建一个包含所有2 n个 可能和的CTE,然后为每个找到最接近的一个TOTAL:
1个
2
3
4
5
6
7
8
9 – All the possible 2N sums
WITH sums(sum, max_id, calc) AS (…)

– Find the best sum per “TOTAL”
SELECT
totals.total,
something_something(total - sum) AS best,
something_something(total - sum) AS calc
FROM draw_the_rest_of_the_bleep_owl

7.封顶总计

到目前为止,我们已经看到了如何使用窗口函数使用SQL计算“常规”运行总计。那很简单。现在,如果我们将运行总计设置为永远不会低于零,那又如何呢?本质上,我们要计算此:

DATE 金额 总计
2012-01-01 800 800
2012-02-01 1900年 2700
2012-03-01 1750 4450
2012-04-01 -20000 0
2012-05-01 900 900
2012-06-01 3900 4800
2012-07-01 -2600 2200
2012-08-01 -2600 0
2012-09-01 2100 2100
2012-10-01 -2400 0
2012-11-01 1100 1100
2012-12-01 1300 2400

所以,当大的负面AMOUNT -20000减去,而不是显示真实TOTAL的-15550,我们简单地展示0。换句话说(或数据集):

DATE 金额 总计
2012-01-01 800 800
2012-02-01 1900年 2700
2012-03-01 1750 4450
2012-04-01 -20000 0
2012-05-01 900 900
2012-06-01 3900 4800
2012-07-01 -2600 2200
2012-08-01 -2600 0
2012-09-01 2100 2100
2012-10-01 -2400 0
2012-11-01 1100 1100
2012-12-01 1300 2400

8.时间序列模式识别

如果您涉及欺诈检测或对大型数据集进行实时分析的任何其他领域,那么时间序列模式识别对于您而言当然不是一个新名词。
如果我们查看“系列长度”数据集,则可能需要在时间序列上针对复杂事件生成触发器,如下所示:
| ID | VALUE_DATE | 金额| LEN | 触发
| ------ | ------------ || --------- | ----- | --------
| 9997 | 2014-03-18 | + 99.17 | 1 |
| 9981 | 2014-03-16 | -71.44 | 4 |
| 9979 | 2014-03-16 | -94.60 | 4 | X
| 9977 | 2014-03-16 | -6.96 | 4 |
| 9971 | 2014-03-15 | -65.95 | 4 |
| 9964 | 2014-03-15 | + 15.13 | 3 |
| 9962 | 2014-03-15 | + 17.47 | 3 |
| 9960 | 2014-03-15 | + 3.55 | 3 |
| 9959 | 2014-03-14 | -32.00 | 1 |

9.枢纽和取消枢纽

如果您已经读了那么多内容,那么以下内容将非常简单:
这是我们的数据,即演员,电影名称和电影等级:

NAME 标题 评分
A.格兰特 匿名身份 G
A.格兰特 尊敬的母亲 PG
A.格兰特 荣耀追踪 PG-13
A. HUDSON **绝地武士 PG
A. CRONYN 铁月亮 PG
A. CRONYN 女士舞台 PG
B. WALKEN SIEGE MADRE R

这就是我们所说的透视:

NAME NC-17 PG G PG-13 R
A.格兰特 3 6 5 3 1
A. HUDSON 12 4 7 9 2
A. CRONYN 6 9 2 6 4
B. WALKEN 8 8 4 7 3
B. WILLIS 5 5 14 3 6
C. DENCH 6 4 5 4 5
C. NEESON 3 8 4 7 3

观察我们如何按演员分组,然后“透视”每个演员所播放的每个评分的数字电影。与其以“关系”方式(即每个组都是一行)显示,我们将整个事物透视成一个整体。每组列。我们可以这样做,因为我们事先知道所有可能的组。
取消透视是相反的,当从上面开始时,我们想回到“每组行数”表示形式:

NAME 评分 COUNT
A.格兰特 NC-17 3
A.格兰特 PG 6
A.格兰特 G 5
A.格兰特 PG-13 3
A.格兰特 R 6
A. HUDSON NC-17 12
A. HUDSON PG 4

10.滥用XML和JSON

首先JSON只是具有较少功能和较少语法的XML
现在,每个人都知道XML很棒。因此,推论是:JSON不太出色,不要使用JSON。
现在,我们已经解决了这一点,我们可以放心地忽略正在进行的JSON-in-the-the-base-hype炒作(无论如何,你们大多数人都会在五年内后悔),然后转到最后一个示例。如何在数据库中执行XML。
最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。
你认为不可能的10个SQL技巧