gpt4 book ai didi

sql - unicorn 观测结果的部分非规范化

转载 作者:行者123 更新时间:2023-12-04 15:35:27 27 4
gpt4 key购买 nike

有很多研究人员观察着世界上最后剩下的unicorns,即Easter Island。每天,研究人员记录他们发现的 unicorn ,发现日期,每个 unicorn 的婴儿数量以及发现时是否喝醉了。这些分别被上载到中心位置,然后每天向我吐出一个平面文件,其中包含所有新观测值。

我有一个看起来像这样的表,其中包含以下信息:

create table unicorn_observations (
observer_id number not null
, unicorn_id number not null
, created date not null -- date the record was inserted into the database
, lastseen date not null -- date the record was last seen
, observation_date date not null
, no_of_babies number not null
, drunk varchar2(1) not null
, constraint pk_uo primary key ( observer_id, unicorn_id, created )
, constraint chk_uo_babies check ( no_of_babies >= 0 )
, constraint chk_uo_drunk check ( drunk in ('y','n') )
);

该表在 observer_idunicorn_idobservation_datelastseen上分别是唯一的。

有时,管理数据输出的 Cobold [sic]会稍微出错,然后将相同的数据重新输出两次。在这种情况下,我将更新 lastseen而不是创建新记录。我仅在每列都相同的情况下执行此操作

不幸的是,研究人员并未完全意识到第三个范式。每个月,即使没有新的观察结果,他们也会上传一些 unicorn 的前几个月观察结果。他们使用新的 observation_date进行此操作,这意味着将一条新记录插入到表中。

我有一个单独的 createdlastseen,以实现完全可追溯性,因为研究人员有时会晚些提交一些观察结果。这些由数据库创建,不属于提交的信息。

这是一些示例数据(列名进行了部分更改,以使其在没有滚动条的情况下适合使用)。
+--------+--------+-----------+-----------+-----------+---------+-------+| OBS_ID | UNI_ID |  CREATED  | LASTSEEN  | OBS_DATE  | #BABIES | DRUNK |+--------+--------+-----------+-----------+-----------+---------+-------+|      1 |      1 | 01-NOV-11 | 01-NOV-11 | 31-OCT-11 |      10 | n     ||      1 |      2 | 01-NOV-11 | 01-NOV-11 | 31-OCT-11 |      10 | n     ||      1 |      3 | 01-NOV-11 | 01-NOV-11 | 31-OCT-11 |      10 | n     ||      1 |      6 | 10-NOV-11 | 10-NOV-11 | 07-NOV-11 |       0 | n     ||      1 |      1 | 17-NOV-11 | 17-NOV-11 | 09-APR-11 |      10 | n     ||      1 |      2 | 17-NOV-11 | 17-NOV-11 | 09-APR-11 |      10 | n     ||      1 |      3 | 17-NOV-11 | 17-NOV-11 | 09-APR-11 |      10 | n     ||      1 |      6 | 17-NOV-11 | 17-NOV-11 | 17-NOV-11 |       0 | n     ||      1 |      6 | 01-DEC-11 | 01-DEC-11 | 01-DEC-11 |       0 | n     ||      1 |      6 | 01-JAN-12 | 01-JAN-12 | 01-JAN-12 |       3 | n     ||      1 |      6 | 01-FEB-12 | 01-FEB-12 | 01-FEB-12 |       0 | n     ||      1 |      6 | 01-MAR-12 | 01-MAR-12 | 01-MAR-12 |       0 | n     ||      1 |      6 | 01-APR-12 | 01-APR-12 | 01-APR-12 |       0 | n     ||      1 |      1 | 19-APR-12 | 19-APR-12 | 19-APR-12 |       7 | y     ||      1 |      2 | 19-APR-12 | 19-APR-12 | 19-APR-12 |       7 | y     ||      1 |      3 | 19-APR-12 | 19-APR-12 | 19-APR-12 |       7 | y     ||      1 |      6 | 01-MAY-12 | 01-MAY-12 | 01-MAY-12 |       0 | n     |+--------+--------+-----------+-----------+-----------+---------+-------+

I would like to partially denormalise these observations so that if a new record is received with the same observer_id, unicorn_id, no_of_babies and drunk (the payload) but with a newer observation_date I update a new column in the table, last_observation_date, instead of inserting a new record. I would still update thelastseen in this situation.

I need to do this as I have a number of complicated unicorn related queries that join to this table; the researchers upload old observations with new dates about 10m times a month and I receive approximately 9m genuinely new records a month. I've been running for a year and already have 225m unicorn observations. As I only need to know the last observation date for each payload combination I would rather massively reduce the size of the table and save myself a lot of time full-scanning it.

This means that the table would become:

create table unicorn_observations (
observer_id number not null
, unicorn_id number not null
, created date not null -- date the record was inserted into the database
, lastseen date not null -- date the record was last seen
, observation_date date not null
, no_of_babies number not null
, drunk varchar2(1) not null
, last_observation_date date
, constraint pk_uo primary key ( observer_id, unicorn_id, created )
, constraint chk_uo_babies check ( no_of_babies >= 0 )
, constraint chk_uo_drunk check ( drunk in ('y','n') )
);

表中存储的数据如下所示;如果观察值仅被“看到”一次,那么 last_observation_date是否为null无关紧要。我在加载数据时不需要帮助,仅需要将当前表进行部分非规范化即可。
+--------+--------+-----------+-----------+-----------+---------+-------+-------------+| OBS_ID | UNI_ID |  CREATED  | LASTSEEN  | OBS_DATE  | #BABIES | DRUNK | LAST_OBS_DT |+--------+--------+-----------+-----------+-----------+---------+-------+-------------+|      1 |      6 | 10-NOV-11 | 01-DEC-11 | 07-NOV-11 |       0 | n     | 01-DEC-11   ||      1 |      1 | 01-NOV-11 | 17-NOV-11 | 09-APR-11 |      10 | n     | 31-OCT-11   ||      1 |      2 | 01-NOV-11 | 17-NOV-11 | 09-APR-11 |      10 | n     | 31-OCT-11   ||      1 |      3 | 01-NOV-11 | 17-NOV-11 | 09-APR-11 |      10 | n     | 31-OCT-11   ||      1 |      6 | 01-JAN-12 | 01-JAN-12 | 01-JAN-12 |       3 | n     |             ||      1 |      6 | 01-FEB-12 | 01-MAY-12 | 01-FEB-12 |       0 | n     | 01-MAY-12   ||      1 |      1 | 19-APR-12 | 19-APR-12 | 19-APR-12 |       7 | y     |             ||      1 |      2 | 19-APR-12 | 19-APR-12 | 19-APR-12 |       7 | y     |             ||      1 |      3 | 19-APR-12 | 19-APR-12 | 19-APR-12 |       7 | y     |             |+--------+--------+-----------+-----------+-----------+---------+-------+-------------+

The obvious answer

select observer_id as obs_id
, unicorn_id as uni_id
, min(created) as created
, max(lastseen) as lastseen
, min(observation_date) as obs_date
, no_of_babies as "#BABIES"
, drunk
, max(observation_date) as last_obs_date
from unicorn_observations
group by observer_id
, unicorn_id
, no_of_babies
, drunk

无法运作,因为它忽略了在2012年1月1日对3个 unicorn 婴儿进行的 unicorn 6观测;这又意味着在11月10日创建的记录的 lastseen不正确。

+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +
| OBS_ID | UNI_ID |创建|最新| OBS_DATE | #婴儿|醉酒| LAST_OBS_DT |
+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +
| 1 | 1 | 11年11月1日| 11年11月17日| 2011年4月9日| 10 | n | 11月10日|
| 1 | 2 | 11年11月1日| 11年11月17日| 2011年4月9日| 10 | n | 11月10日|
| 1 | 3 | 11年11月1日| 11年11月17日| 2011年4月9日| 10 | n | 11月10日|
| 1 | 6 | 11年11月10日| 2012年5月1日| 11年11月7日| 0 | n | 2012年5月1日|
| 1 | 6 | 2012年1月1日| 2012年1月1日| 2012年1月1日| 3 | n | 2012年1月1日|
| 1 | 1 | 12-APR-12 | 12-APR-12 | 12-APR-12 | 7 | y | 12-APR-12 |
| 1 | 2 | 12-APR-12 | 12-APR-12 | 12-APR-12 | 7 | y | 12-APR-12 |
| 1 | 3 | 12-APR-12 | 12-APR-12 | 12-APR-12 | 7 | y | 12-APR-12 |
+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +

我目前看不到没有一些程序逻辑即循环的方法。我宁愿避免这种情况下的循环,因为我必须对225m行表进行全扫描260次(不同 created日期的数量)。即使使用 lag()lead(),也需要进行递归,因为每个 unicorn 的观察次数不确定。

有没有一种方法可以在单个SQL语句中创建此数据集?

表规范和样本数据也位于 SQL Fiddle中。

尝试更好的解释:

问题是什么时候是正确的。在2012年1月1日, unicorn 6号有3个婴儿。

仅查看GROUP BY创建的“表格”中的 unicorn 6;如果我试图在1月1日找到婴儿的数量,我将获得两张记录,这是矛盾的。

+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +
| OBS_ID | UNI_ID |创建|最新| OBS_DATE | #婴儿|醉酒| LAST_OBS_DT |
+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +
| 1 | 6 | 11年11月10日| 2012年5月1日| 11年11月7日| 0 | n | 2012年5月1日|
| 1 | 6 | 2012年1月1日| 2012年1月1日| 2012年1月1日| 3 | n | 2012年1月1日|
+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +

但是,我只想要一行,如第二张表所示。在这里,对于任何时间点,最多只有一个“正确”值,因为 unicorn 6有0个婴儿的两个时间段在有3个 child 的那一天被分成了两行。

+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +
| OBS_ID | UNI_ID |创建|最新| OBS_DATE | #婴儿|醉酒| LAST_OBS_DT |
+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +
| 1 | 6 | 11年11月10日| 2011年12月1日| 11年11月7日| 0 | n | 2011年12月1日|
| 1 | 6 | 2012年1月1日| 2012年1月1日| 2012年1月1日| 3 | n | |
| 1 | 6 | 01-FEB-12 | 2012年5月1日| 01-FEB-12 | 0 | n | 2012年5月1日|
+ -------- + -------- + ----------- + ----------- + ------- ---- + --------- + ------- + ------------- +

1.放牧

最佳答案

基于我认为您正在尝试做的事情,主要是基于有关 unicorn 6特定问题的更新,我认为这可以达到您想要的结果。它不需要递归leadlag,但是需要两个级别。

select *
from (
select observer_id, unicorn_id,
case when first_obs_dt is null then created
else lag(created) over (order by rn) end as created,
case when last_obs_dt is null then lastseen
else lead(lastseen) over (order by rn) end as lastseen,
case when first_obs_dt is null then observation_date
else lag(observation_date) over (order by rn)
end as observation_date,
no_of_babies,
drunk,
case when last_obs_dt is null then observation_date
else null end as last_obs_dt
from (
select observer_id, unicorn_id, created, lastseen,
observation_date, no_of_babies, drunk,
case when lag_no_babies != no_of_babies or lag_drunk != drunk
or lag_obs_dt is null then null
else lag_obs_dt end as first_obs_dt,
case when lead_no_babies != no_of_babies or lead_drunk != drunk
or lead_obs_dt is null then null
else lead_obs_dt end as last_obs_dt,
rownum rn
from (
select observer_id, unicorn_id, created, lastseen,
observation_date, no_of_babies, drunk,
lag(observation_date)
over (partition by observer_id, unicorn_id, no_of_babies,
drunk
order by observation_date) lag_obs_dt,
lag(no_of_babies)
over (partition by observer_id, unicorn_id, drunk
order by observation_date) lag_no_babies,
lag(drunk)
over (partition by observer_id, unicorn_id, no_of_babies
order by observation_date) lag_drunk,
lead(observation_date)
over (partition by observer_id, unicorn_id, no_of_babies,
drunk
order by observation_date) lead_obs_dt,
lead(no_of_babies)
over (partition by observer_id, unicorn_id, drunk
order by observation_date) lead_no_babies,
lead(drunk)
over (partition by observer_id, unicorn_id, no_of_babies
order by observation_date) lead_drunk
from unicorn_observations
order by 1,2,5
)
)
where first_obs_dt is null or last_obs_dt is null
)
where last_obs_dt is not null
order by 1,2,3,4;

这使:
OBSERVER_ID UNICORN_ID CREATED   LASTSEEN  OBSERVATI NO_OF_BABIES D LAST_OBS_
----------- ---------- --------- --------- --------- ------------ - ---------
1 1 17-NOV-11 01-NOV-11 09-APR-11 10 n 31-OCT-11
1 1 19-APR-12 19-APR-12 19-APR-12 7 y 19-APR-12
1 2 17-NOV-11 01-NOV-11 09-APR-11 10 n 31-OCT-11
1 2 19-APR-12 19-APR-12 19-APR-12 7 y 19-APR-12
1 3 17-NOV-11 01-NOV-11 09-APR-11 10 n 31-OCT-11
1 3 19-APR-12 19-APR-12 19-APR-12 7 y 19-APR-12
1 6 10-NOV-11 01-DEC-11 07-NOV-11 0 n 01-DEC-11
1 6 01-JAN-12 01-JAN-12 01-JAN-12 3 n 01-JAN-12
1 6 01-FEB-12 01-MAY-12 01-FEB-12 0 n 01-MAY-12

9 rows selected.

它已经获得了 unicorn 6的三个记录,但是第三个记录的 lastseenobservation_date与您的示例相反,因此我不确定是否仍然不了解这一点。我假设您想在每个分组中保留最早的 observation_date和最新的 lastseen,理由是添加新记录时似乎会发生这种情况,但是我不确定...

因此,最里面的查询从表中获取原始数据,并使用稍微不同的分区获取 leadlagobservation_date列的 no_of_babiesdrunkorder by是一个 rownum,因此可以在以后使用,在下一步中获得并在之后的顺序中使用。为了简洁起见,只为 unicorn 6:
CREATED   LASTSEEN  OBSERVATI NO_OF_BABIES D LAG_OBS_D LAG_NO_BABIES L LEAD_OBS_ LEAD_NO_BABIES L
--------- --------- --------- ------------ - --------- ------------- - --------- -------------- -
10-NOV-11 10-NOV-11 07-NOV-11 0 n 17-NOV-11 0 n
17-NOV-11 17-NOV-11 17-NOV-11 0 n 07-NOV-11 0 n 01-DEC-11 0 n
01-DEC-11 01-DEC-11 01-DEC-11 0 n 17-NOV-11 0 n 01-FEB-12 3 n
01-JAN-12 01-JAN-12 01-JAN-12 3 n 0 0
01-FEB-12 01-FEB-12 01-FEB-12 0 n 01-DEC-11 3 n 01-MAR-12 0 n
01-MAR-12 01-MAR-12 01-MAR-12 0 n 01-FEB-12 0 n 01-APR-12 0 n
01-APR-12 01-APR-12 01-APR-12 0 n 01-MAR-12 0 n 01-MAY-12 0 n
01-MAY-12 01-MAY-12 01-MAY-12 0 n 01-APR-12 0 n

如果 leadlag值已更改,则下一个级别将 observation_datenum_of_babiesdrunk值置为空白-您仅专门提到了对婴儿计数的分割,但是我假设您也希望对清醒度进行分割。此后,任何具有 nullfirst_obs_datelast_obs_date的东西都是小范围的开始或结束。
CREATED   LASTSEEN  OBSERVATI NO_OF_BABIES D FIRST_OBS LAST_OBS_         RN
--------- --------- --------- ------------ - --------- --------- ----------
10-NOV-11 10-NOV-11 07-NOV-11 0 n 17-NOV-11 1
17-NOV-11 17-NOV-11 17-NOV-11 0 n 07-NOV-11 01-DEC-11 2
01-DEC-11 01-DEC-11 01-DEC-11 0 n 17-NOV-11 3
01-JAN-12 01-JAN-12 01-JAN-12 3 n 4
01-FEB-12 01-FEB-12 01-FEB-12 0 n 01-MAR-12 5
01-MAR-12 01-MAR-12 01-MAR-12 0 n 01-FEB-12 01-APR-12 6
01-APR-12 01-APR-12 01-APR-12 0 n 01-MAR-12 01-MAY-12 7
01-MAY-12 01-MAY-12 01-MAY-12 0 n 01-APR-12 8

现在,可以忽略不是最小范围的开始或结尾的任何值,因为这些值与之前或之后的值相同或被它们取代。这处理了不确定的观察数问题-此时您忽略了多少都无所谓。因此,下一个级别通过过滤 first_obs_dtlast_obs_dt都不为空的行来消除这些中间值。在该过滤后的集合中,有第二层 leadlag来获取每个日期的第一个或最后一个值-这是我不确定的一点,因为它与您的一个样本都不匹配。
CREATED   LASTSEEN  OBSERVATI NO_OF_BABIES D LAST_OBS_
--------- --------- --------- ------------ - ---------
10-NOV-11 01-DEC-11 07-NOV-11 0 n
10-NOV-11 01-DEC-11 07-NOV-11 0 n 01-DEC-11
01-JAN-12 01-JAN-12 01-JAN-12 3 n 01-JAN-12
01-FEB-12 01-MAY-12 01-FEB-12 0 n
01-FEB-12 01-MAY-12 01-FEB-12 0 n 01-MAY-12

最后,将其余没有 last_obs_dt的行过滤掉。

现在,我将等待看看我误解了哪些位... * 8-)

纠正 leadlag顺序后, unicorn 1的每个阶段的信息相同:
CREATED   LASTSEEN  OBSERVATI NO_OF_BABIES D LAG_OBS_D LAG_NO_BABIES L LEAD_OBS_ LEAD_NO_BABIES L
--------- --------- --------- ------------ - --------- ------------- - --------- -------------- -
17-NOV-11 17-NOV-11 09-APR-11 10 n 31-OCT-11 10 n
01-NOV-11 01-NOV-11 31-OCT-11 10 n 09-APR-11 10 n
19-APR-12 19-APR-12 19-APR-12 7 y

CREATED LASTSEEN OBSERVATI NO_OF_BABIES D FIRST_OBS LAST_OBS_ RN
--------- --------- --------- ------------ - --------- --------- ----------
17-NOV-11 17-NOV-11 09-APR-11 10 n 31-OCT-11 1
01-NOV-11 01-NOV-11 31-OCT-11 10 n 09-APR-11 2
19-APR-12 19-APR-12 19-APR-12 7 y 3

CREATED LASTSEEN OBSERVATI NO_OF_BABIES D LAST_OBS_
--------- --------- --------- ------------ - ---------
17-NOV-11 17-NOV-11 09-APR-11 10 n 09-APR-11
19-APR-12 19-APR-12 19-APR-12 7 y 19-APR-12

我不确定当原始数据按这样的顺序输入时,保留的 observation_datelastseen会发生什么变化,或者在将来添加新记录的情况下该如何处理。

关于sql - unicorn 观测结果的部分非规范化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12723776/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com