SAP HANA PROCEDURE(sap hana数据库 存储过程)

存储过程

内容引自HANA SQLScript第三部分储存过程-创建储存过程是真长

3.1CREATE PROCEDURE

1
2
3
4
5
6
7
8
CREATE PROCEDURE [<schema_name>.]<proc_name> [(<parameter_clause>)] [LANGUAGE <lang>] [SQL SECURITY <mode>]
[DEFAULT SCHEMA <default_schema_name>][READS SQL DATA [WITH RESULT VIEW ]] AS

BEGIN [SEQUENTIAL EXECUTION]

<procedure_body>

END

The CREATE PROCEDURE statement creates a procedure using the specified programming language .

DeepL翻译:CREATE PROCEDURE语句使用指定的编程语言创建一个过程。

3.1.1

  1. ::= [{,}…]
    1. ::= []
      1. ::= IN|OUT|INOUT
      2. ::= | |
        1. ::= DATE | TIME| TIMESTAMP | SECONDDATE | TINYINT | SMALLINT |INTEGER | BIGINT | DECIMAL | SMALLDECIMAL | REAL | DOUBLE
          | VARCHAR | NVARCHAR | ALPHANUM | VARBINARY | CLOB | NCLOB | BLOB
        2. ::= TABLE ()
          1. ::= [{, }…]
            1. ::=

输入或输出参数类型可以是标准的基础数据类型与表类型,但INOUT参数只能是标准数据类型

:默认为IN

:参数类型如果是表类型,这个表类型可以是预先使用Create Type…As Table语句创建的类型对象,也可以在指定存储过程参数类型时才定义表类型

3.1.2 LANGUAGE

::= SQLSCRIPT | R

默认为: SQLSCRIPT。指定存储过程实现的程序语言

3.1.3 SQL SECURITY

::= DEFINER | INVOKER

默认: DEFINER,指定存储过程的安全模式

DEFINER:Specifies that the execution of the procedure is performed with the privileges of the definer of the procedure.

DEFINER:只有存储过程的定义者才能执行?

INVOKER:Specifies that the execution of the procedure is performed with the privileges of the invoker of the procedure.

3.1.4 READS SQL DATA

存储过程为只读的,不能包含DDL与DML(INSERT、UPDATE、DELETE)语句(即只能使用查询SQL与DCL语句),如果调用其他存储过程,则被调用过程也是只读的。设置参数会有特定的优化

3.1.5 WITH RESULT VIEW

Specifies the result view to be used as the output of a read-only procedure.将只读取存储过程的输出看做结果视图

When a result view is defined for a procedure, it can be called by an SQL statement in the same way as a tableor view. See Example 2 - Using a result view below.定义了结果视图的存储过程,可以被其他查询SQL用来查询,此时存储过程就像一个表或视图

3.1.6 SEQUENTIAL EXECUTION

This statement will force sequential execution of the procedure logic. No parallelism takes place.不允许存储过程并行执行

3.1.7

::= [] 定义块
[] 异常处理块
存储体语句块

Defines the main body of the procedure according to the programming language selected.过程的主体由设定的程序语言来定义

3.1.7.1 定义变量

  1. ::= [{}…]
    1. ::= DECLARE {|||} ;
      1. <proc_variable>::= <variable_name_list> [**CONSTANT**] {<sql_type>|<array_datatype>}[NOT NULL][<proc_default>]
        1. ::= [{, <variable_name}…]
        2. ::= ARRAY [ = ]
          1. ::= ARRAY ( [ { , }…] )
        3. ::= (DEFAULT | ‘=’ ) |
      2. ::= {|}
        1. ::= [{, <variable_name}…]
        2. ::= TABLE()
          1. ::= ([{,}…])
      3. ::= CURSOR [ ( proc_cursor_param_list ) ] FOR ;
        1. ::= [{,}…]
          1. ::=
      4. ::= CONDITION | CONDITION FOR

CREATE procedure proc() LANGUAGE SQLSCRIPT AS

BEGIN

declare a int****default 2;–基本类型变量使用前一定要定义

​ – 如果某个变量是表类型的变量,可以不用声明,直接就可以使用,这与基本类型变量是不一样的

​ – ——基本类型变量使用前需要定义

​ –declare tab_var1 table(a int,b int); 表类型变量可不定义就使用

​ a := 1;

​ – a = 1; 基本类型变量赋值时,等号前一定要加冒号,这与表类型变量恰好相反

​ tab_var1 = select 1 as a,2 as b from dummy;

​ –tab_var1 := select 1 as a,2 as b from dummy; 这是错误,表类型变量赋值时,只能使用等号,不能在等号前加冒号,这与基本类型变量赋值相反

END;

暂时看到这了:太长了下次再接着看

3.1.7.2 异常处理

::= [{, }…]

::= DECLARE EXIT HANDLER FOR ;

::= {,}…]

::= SQLEXCEPTION | SQLWARNING | |

3.1.7.3 过程体

::= {}…

::= | | | | | | | |
| | | | | | | | | | |

3.1.7.3.1 内嵌块

::= BEGIN [][] END ;

::= [SEQUENTIAL EXECUTION] [AUTONOMOUS TRANSACTION]| [AUTONOMOUS TRANSACTION] [SEQUENTIAL
EXECUTION]

内嵌块, BEGIN 、 END是可以内嵌的

3.1.7.3.2 给变量赋值

::= = { | } ;| ‘[‘ ‘]’ = ;

= ARRAY_AGG ( . [ ORDER BY ] ) | CARDINALITY ( ) | TRIM_ARRAY
( , ) | ARRAY ( )

::=< array_variable_name > [{, < array_variable_name >}…] 这个是自己 加的,是否正确?

Assign values to variables. An can be either a simple expression, such as a character, a date, or a number, or it can be a scalar function or a scalar user-defined function.将值赋给变量。可以是一个简单的表达式(如字符、日期、数字表达式),或者是标准函数,或用户自定义函数

有关 ARRAY 、ARRAY_AGG、CARDINALITY、TRIM_ARRAY函数请参考后面数组函数

3.1.7.3.3 单个赋值

::= = | = | = | =

::= TRACE ( ) ;| CE_LEFT_OUTER_JOIN ( , ,’[‘ ‘]’ ] ) ;
| CE_RIGHT_OUTER_JOIN ( , ,’[‘ ‘]’ [ ] ) ; | CE_FULL_OUTER_JOIN ( , ,’[‘ ‘]’ [ ] ); | CE_JOIN ( , , ‘[‘ ‘]’ [] ) ;| CE_UNION_ALL ( , ) ;| CE_COLUMN_TABLE ( [ ] ) ; | CE_JOIN_VIEW ( [ ] ) ;| CE_CALC_VIEW ( [ ] ) ; | CE_OLAP_VIEW ( [ ] ) ; | CE_PROJECTION ( , ‘[‘ ‘]’ ) ; | CE_PROJECTION ( ) ; | CE_AGGREGATION ( , ‘[‘ ‘]’ [ ] ); | CE_CONVERSION ( , ‘[‘ ‘]’ [ ] ) ;| CE_VERTICAL_UNION ( , ‘[‘ ‘]’ ) ;| CE_COMM2R ( , , , , , ) ;

::= [.]

::= APPLY_FILTER ( { | }, ) ;

::= UNNEST ( ) [ WITH ORDINALITY ][] ;

::= [{, }…]

::= AS [table_name] ( )

::= [{, }…]

WITH ORDINALTIY:Appends an ordinal column to the return values.

3.1.7.3.4 多个赋值

::= () =

Assign values to a list of variables with only one function evaluation. For example, must be a scalar user defined function and the number of elements in must be equal to the number of output parameters of the scalar UDF(用户定义函数).

:用户自定义函数

函数的输入参数个数需与中的参数个数相同

3.1.7.3.5

::= IF THEN [SEQUENTIAL EXECUTION][] [] [] [] END IF ;

::= ELSEIF THEN [SEQUENTIAL EXECUTION][] []

::= ELSE [SEQUENTIAL EXECUTION][][]

You use IF - THEN - ELSE IF to control execution flow with conditionals.

3.1.7.3.6

::= LOOP [SEQUENTIAL EXECUTION][] [] END LOOP ;

You use loop to repeatedly execute a set of statements.

3.1.7.3.7

::= WHILE DO [SEQUENTIAL EXECUTION][] [] END WHILE ;

You use while to repeatedly call a set of trigger statements while a condition is true.

3.1.7.3.8

::= FOR IN [ REVERSE ] […] DO [SEQUENTIAL EXECUTION]

[]

[]

END FOR ;

You use FOR - IN loops to iterate over a set of data.

3.1.7.3.9

::= FOR AS [] DO [SEQUENTIAL EXECUTION]

[]

[]

END FOR ;

::= ( [ { , }…] )

You use FOR - EACH loops to iterate over all elements in a set of data.

3.1.7.3.10

::= BREAK ;

Terminates a loop.结束循环

3.1.7.3.11

::= CONTINUE ;

Skips a current loop iteration and continues with the next value.结束当前循环继续下一次循环

3.1.7.3.12

::= SIGNAL [] ;

You use the SIGNAL statement to explicitly raise an exception from within your trigger procedures.

3.1.7.3.13

::= RESIGNAL [] [] ;

You use the RESIGNAL statement to raise an exception on the action statement in an exception handler. If an error code is not specified, RESIGNAL will throw the caught exception.重新抛出异常?

::= |

::=

::=

You can SIGNAL or RESIGNAL a signal name or an SQL error code.

::= SET MESSAGE_TEXT = ‘

::=

You use SET MESSAGE_TEXT to deliver an error message to users when specified error is thrown during procedure execution.

3.1.7.3.14

::= | | | | | | | |

, ,, and 请参考HANA SQL中INSERT , DELETE, UPDATE, REPLACE and UPSERT SQL语句

::= SELECT INTO [] [] [] [{
, … }] [] [] ;

::= [{, }…] 为基础类型变量

3.1.7.3.15

::= OPEN [ ] ;

::= FETCH INTO ;

::= CLOSE ;

Cursor operations

3.1.7.3.16

::= CALL () ;

Calling a procedure

3.1.7.3.17

::= {EXEC | EXECUTE IMMEDIATE} ;

You use EXEC to make dynamic SQL calls.

3.1.7.3.18

::= RETURN [] ;

Return a value from a procedure.

3.1.8 Examples

Example 1 - Creating an SQL Procedure

You create an SQLScript procedure with the following definition.

CREATECOLUMNTABLE“SYSTEM”.”T” (“ID”INTEGER CS_INT,

​ “NAME”VARCHAR(30),

​ “PAYMENT”INTEGER CS_INT) UNLOAD****PRIORITY 5 AUTO MERGE;

insert****into“SYSTEM”.”T”values(1,’a’,10);

insert****into“SYSTEM”.”T”values(2,’b’,20);

insert****into“SYSTEM”.”T”values(3,’c’,30);

CREATE****PROCEDURE orchestrationProc LANGUAGE SQLSCRIPT AS

BEGIN

DECLARE v_id INT;

DECLARE v_name VARCHAR(30);

DECLARE v_pmnt INT;

DECLARE v_msg VARCHAR(200);

DECLARE****CURSOR c_cursor1 (p_payment INT) FOR****SELECT id, name, PAYMENT FROM t

WHERE payment >= p_payment ORDER****BY id ASC;

OPEN c_cursor1(20);

FETCH c_cursor1 INTO v_id, v_name, v_pmnt;

​ v_msg := v_name || ‘ (id ‘ || v_id || ‘) earns ‘ || v_pmnt || ‘ $.’;

select v_msg from dummy;

CLOSE c_cursor1;

end;

Example 2 - Using a result view

创建带返回视图结果的存储过程:

CREATE****PROCEDURE ProcWithResultView(IN id INT, OUT o1 t)

LANGUAGE SQLSCRIPT

READS SQL DATA WITH RESULT VIEW ProcView AS

BEGIN

o1 = SELECT * FROM t WHERE id = :id;

END;

调用存储视图:

select * from ProcView with parameters (‘placeholder’ = (‘

idid

‘ ,’2’ ));

CREATE****PROCEDURE Proc_test(IN id INT,IN name varchar(3),OUT o1 “SYSTEM”.”T_COLUMN”)

LANGUAGE SQLSCRIPT

READS SQL DATA WITH RESULT VIEW Proc_View AS

BEGIN

outt = CE_COLUMN_TABLE(“SYSTEM”.”T_COLUMN”,[id,name,payment]);

–注:过滤条件一定要使用单引号整体引起来;列名一定要使用双引号引起来;

–如果是字符串,则要使用单引号引起来,因为外层有单引号,所以里面要使用两个单引号代表一个单引号

–o1 = CE_PROJECTION(:outt,[id,name,payment],’”ID” = :id AND “NAME” = ‘’:name’’’);

–上面这句等效于下面两句

o1 = CE_PROJECTION(:outt,[id,name,payment],’”ID” = :id’);

o1 = CE_PROJECTION(:o1,[id,name,payment],’”NAME” = ‘’:name’’’);

END;

select * from Proc_View with parameters (‘placeholder’ = (‘

idid

‘ ,’2’ ),’placeholder’ = (‘namename’ ,’b’ ));

Note

Procedures and result views produced by procedures are not connected from the security perspective and therefore do not inherit privileges from each other. The security aspects of each object must be handled separately. For example, you must grant the SELECT privilege on a result view and EXECUTE privilege on a connected procedure.

7a1c08c79f3df8dccd0ae9edda11728b461028ed

Oracle中的translate函数和replace函数的用法

接触到此函数目的是为了判断某一单元格数据是在这里插入代码片否为纯数字,虽然实际来说TRANSLATE()实际不是为了判断数据格式。

translate函数语法:

1
translate(expr, from_string, to_string)

功能:在expr中寻找匹配from_string中符合的字符下标,随后根据下标去to_string中寻找对应的字符替换掉expr中对应下标的字符。

expr:进行处理匹配替换的数据

from_string:需要在expr中寻找匹配的字符

to_string:替换在from_string中找到的expr中对应下标的字符

栗子:

1
2
select translate('ac','ab2','00') from dual;
--return 0c

首先拿出ac中的a去匹配ab2中的字符,他会一个字符一个字符的进行判断。

1
2
3
a==a true      	//a和ab2中的第一个字符比较,结果正确
a==b false //a和ab2中的第二个字符比较,结果错误
a==2 false //a和ab2中的第三个字符比较,结果错误

而其中的a匹配到了ab2中的字符,a在ab2中为第一位,所以也去00中寻找第一位进行替换,这时是0c。

再去匹配第二位的c,而在ab2中没有找到,所以不进行替换。

所以返回的结果为0c。

栗子ii:

1
2
select translate('ac1','abc123','abc') from dual;
--return ac

当to_string比from_string短时,会对多余的位数进行剔除

这里中进行匹配到最后的1时,发现1在abc123中位置为第四位,而通过第四位去替换,去寻找abc的第四位时发现abc没有第四位,这时会将1剔除,返回的结果为ac。

需要注意的是,to_str不能是NULL或者’’,否则会返回空值。

  1. translate也不能用于CLOB,再说得简单一点,也就是
    如果string中的字符如果在from_string中没有,那么返回时被保留
  2. 如果string中的字符是from_string中的”额外字符”,那么返回时被删除
  3. 如果string中的字符在from_string中找到,且在to_string中有相应位置的字符,那么返回时用to_string中的字符替换string中的字符

replace函数

语法:

1
REPLACE(expr, from_string,to_string)

功能:用法和上文中的translate的用法相同,唯一不同的是translate是会对expr中的字符一个一个和from_string进行匹配,而replace却类似于模糊查询like,会在expr中查找包含from_string的部分并且进行替换为to_string。

expr:进行处理匹配替换的数据

from_string:需要在expr中寻找匹配的字符

to_string:替换在from_string中找到的expr中对应下标的字符

栗子:

1
2
3
4
select REPLACE('fgsgswsgs', 'fk' ,'j') 返回值 from dual;
--return fgsgswsgs
select REPLACE('fgsgswsgs', 'sg' ,'eeerrrttt') 返回值 from dual;
--return fgeeerrrtttsweeerrrttts

分析:

第一个例子中由于’fgsgswsgs’中没有与’fk’匹配的字符串,

​ 故返回值仍然是’fgsgswsgs’;

​ 第二个例子中将’fgsgswsgs’中的字符串’sg’全部转换为’eeerrrttt’。

总结:综上所述,replace与translate都是替代函数,

​ 只不过replace针对的是字符串,而translate针对的是单个字符。

和replace函数的区别

1
2
3
4
5
select translate('itmyhome#163%com', '#%', '@.') from dual;
--return itmyhome@163.com

select replace('itmyhome#163%com', '#%', '@.') from dual;
--return itmyhome#163%com

上面的translate函数是将#替换为@,%替换为.

而replace却没有实现此效果,是因为没有找到#%整体的组合

部分引自

53186325_p0

戦いの前夜

python爬虫-爬取steam统计数据(steam游戏在线人数)

转自[阿里波特]-[Python爬虫小白入门]

前言


你是不是在为想收集数据而不知道如何收集而着急?

你是不是在为想学习爬虫而找不到一个专门为小白写的教程而烦恼?

Bingo! 你没有看错,这就是专门面向小白学习爬虫而写的!我会采用实例的方式,把每个部分都跟实际的例子结合起来帮助小伙伴儿们理解。最后再写几个实战的例子。

我们使用Python来写爬虫,一方面因为Python是一个特别适合变成入门的语言,另一方面,Python也有很多爬虫相关的工具包,能够简单快速的开发出我们的小爬虫。
本系列采用Python3.5版本,毕竟2.7会慢慢退出历史舞台~

那么,接下来,你得知道什么是爬虫、爬虫从哪里爬取数据的,以及,学习爬虫都要学习哪些东西。

什么是爬虫


来看看百度百科是如何定义的

网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫

什么?没看懂?没关系,我来给你解释一下

打开一个网页,里面有网页内容吧,想象一下,有个工具,可以把网页上的内容获取下来,存到你想要的地方,这个工具就是我们今天的主角:爬虫。

这样是不是更清晰了呢?

既然了解了爬虫是什么,那么爬虫是如何爬取数据的呢?

爬虫是哪里爬取数据的


打开浏览器(强烈建议谷歌浏览器),找到浏览器地址栏,然后在里输入store.steampowered.com/stats/并回车,你会看到网页内容。

QQ截图20210322162126

摁下键盘上的F12打开开发调试工具,,然后点击元素。看到这些文字了吗?这才是网页最赤果果的样子。

QQ截图20210322162407

其实所有的网页都是HTML代码,只不过浏览器将这些代码解析成了上面的网页,我们的爬虫抓取的其实就是HTML代码中的文本。

随后点击开发调试工具左上角的小鼠标图标,点击你想要爬取的数据,你就会在元素这一栏看到下面红框的地方,是你要爬取数据的本来模样。

QQ截图20210322162801

没错,我们的爬虫抓取的正是网页中的数据,你要知道你想要抓取什么数据,你的目标网站是什么,才可以把想法变成现实的。

学习爬虫的必备知识


大家要先对以下内容有一定的了解再来学习爬虫哦,磨刀不误砍柴工

  • HTML
    这个能够帮助你了解网页的结构,内容等。可以参考W3School的教程
  • Python
    如果有编程基础的小伙伴儿,推荐看一个廖雪峰的Python教程就够了
    没有编程基础的小伙伴,推荐看看视频教程(网易云课堂搜Python),然后再结合廖雪峰的教程,双管齐下。
    其实知乎上总结的已经非常好了,我就不多唠叨了。知乎-如何系统的自学Python
  • TCP/IP协议,HTTP协议
    这些知识能够让你了解在网络请求和网络传输上的基本原理,了解就行,能够帮助今后写爬虫的时候理解爬虫的逻辑。
    廖雪峰Python教程里也有简单介绍,可以参考:TCP/IP简介HTTP协议
    想更深入学习的小伙伴儿可以去网上多搜搜相关的书籍哦

OK, 下一步就开始我们的实战啦

requests 库的安装

为什么要先说Requests库呢,因为这是个功能很强大的网络请求库,可以实现跟浏览器一样发送各种HTTP请求来获取网站的数据。网络上的模块、库、包指的都是同一种东西,所以后文中可能会在不同地方使用不同称谓,不要迷惑。

直接使用Python3.5的小伙伴儿输入这个命令:
pip install requests

如果你机器上存在多个Python版本,要给Python3.5的版本安装requests库,需要输入以下命令:
py -3 -m pip install requests

好啦,requests库安装完毕,接下来我们会在实际例子中演示它的使用。想要深入了解requests模块的小伙伴也可以仔细阅读英文官方文档,和中文官方文档,如果用到该文没有提到的功能,则查看文档即可。

开工


新建一个文件,只要他的后缀为.py即可,名字选择你喜欢的就好。

输入第一行代码来导入requests库:
import requests #导入requests库

然后用它来获取咱们的目标网页:

1
2
r = requests.get('https://store.steampowered.com/stats/') #像目标url地址发送get请求,返回一个response对象
print(r.text) #r.text是http response的网页HTML

之后再文件目录打开cmd页面,指令运行

python xxxxx.py

执行完之后,底部会出现输出结果:

QQ截图20210322163703

可以看到底部是获取到的网页内容。这就完成了爬虫的第一步,获取到了网页的HTML内容。

这用到了requests库的get请求。其他请求使用也与之类似,但本文不进行阐述。

我们刚才用requests库发送http请求获得了网页的HTML内容,那么应该如何从HTML中获得图片?

BeautifulSoup库就此登场啦,赶快去来了解它的用法。

刚才演示了如何使用requests模块向网站发送http请求,获取到网页的HTML数据。这篇来演示如何使用BeautifulSoup模块来从HTML文本中提取我们想要的数据。

模块安装


BeautifulSoup 有多个版本,我们使用BeautifulSoup4。详细使用看BeautifuSoup4官方文档
使用管理员权限打开cmd命令窗口,在窗口中输入下面的命令即可安装:
pip install beautifulsoup4

然后我们安装lxml,这是一个解析器,BeautifulSoup可以使用它来解析HTML,然后提取内容。

pip install lxml

如果不安装lxml,则BeautifulSoup会使用Python内置的解析器对文档进行解析。之所以使用lxml,是因为它速度快。

BeautifulSoup 库的使用


网上找到的几个官方文档:BeautifulSoup4.4.0中文官方文档BeautifulSoup4.2.0中文官方文档。不同版本的用法差不多,几个常用的语法都一样。

首先来看BeautifulSoup的对象种类,在使用的过程中就会了解你获取到的东西接下来应该如何操作。

BeautifulSoup对象的类型

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象。所有对象可以归纳为4种类型: Tag , NavigableString , BeautifulSoup , Comment 。下面我们分别看看这四种类型都是什么东西。

Tag

这个就跟HTML或者XML(还能解析XML?是的,能!)中的标签是一样一样的。我们使用find()方法返回的类型就是这个(插一句:使用find-all()返回的是多个该对象的集合,是可以用for循环遍历的。)。返回标签之后,还可以对提取标签中的信息。

提取标签的名字:
1
tag.name
提取标签的属性:
1
tag['attribute']

NavigableString

NavigableString就是标签中的文本内容(不包含标签)。获取方式如下:
tag.string
还是以上面那个例子,加上下面这行,然后执行:
print('NavigableString is:', find.string)

BeautifulSoup

BeautifulSoup对象表示一个文档的全部内容。支持遍历文档树和搜索文档树。

Comment

这个对象其实就是HTML和XML中的注释。

搜索文档树

最常用的当然是find()和find_all()啦,当然还有其他的。比如find_parent() 和 find_parents()、 find_next_sibling() 和 find_next_siblings() 、find_all_next() 和 find_next()、find_all_previous() 和 find_previous() 等等。
我们就看几个常用的,其余的如果用到就去看官方文档哦。

  • find_all()
    搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。返回值类型是bs4.element.ResultSet。
    完整的语法:
    find_all( name , attrs , recursive , string , **kwargs )

继续之前的爬取


我们选中想要爬取的地方,查看html代码。发现游戏的在线人数都在span标签里,并且class都是currentServers,如下图。

QQ截图20210322165150

通过观察,发现在线人数都在span元素中的文本内容。下面,我们先获取到所有的含有数据的span标签,然后在循环获取span标签中的文本内容。

1
2
3
4
5
6
7
8
9
import requests #导入requests 模块
from bs4 import BeautifulSoup #导入BeautifulSoup 模块

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'} #给请求指定一个请求头来模拟chrome浏览器
web_url = 'https://store.steampowered.com/stats/'
r = requests.get(web_url, headers=headers) #像目标url地址发送get请求,返回一个response对象
all_a = BeautifulSoup(r.text, 'lxml').find_all('span', class_='currentServers') #获取网页中的class为currentServers的所有span标签
for a in all_a:
print(a.string) #循环获取a标签中的style

这里的find_all(‘span’, class_=’currentServers’)是找到所有class为currentServers的span标签,返回的是一个list,所以可以用for循环获取每个span标签。

力荐转自[阿里波特]-[Python爬虫小白入门]OvO

bbe600fa828ba61e7b3276565634970a314e593a

Excalibur!!!

SAP Data Services Designer

SAP DATA SERVICES DESIGNER工具介绍和基础使用(抽表)

接下来简称DS

SAP BusinessObjects Data Service 是通过SAP HANA认证的ETL工具。采用数据批量处理的方式,定期执行后台作业,将数据从多个业务系统中抽取出来,并进行必要的处理(转换,合并,过滤,清晰),然后在加载到HANA数据库中。

DS组件之间的关系:

20200811145254657
Designer 用户可在此界面1. 创建、测试以及执行填充数据仓库的作业
2. 创建并通过拖拽的方法连接对象形成流程图
3. 在流程图中打开编辑修改对象的配置
4. 定义数据映像、转换以及逻辑控制
5. 通过将对象组合到工作流(作业执行定义)和数据流(数据转换定义)中来创建应用程序
Job Server 启动数据服务处理的引擎以及数据服务套件与其他组件的接口的引擎的应用程序。
Engine 执行在Designer中定义的各个作业,以有效地完成已定义的任务。
Repository 存储Designer预定义系统对象和用户定义对象(包括源和目标元数据以及转换规则)的数据库。 创建一个本地存储库,然后创建一个中央存储库,以与其他用户共享对象并进行版本控制。
Access Server 在Web应用程序与数据服务作业服务器和引擎之间传递消息。 提供用于请求响应处理的可靠且可扩展的接口。
Administrator 提供以下基于浏览器的数据服务资源管理的Web管理员:
1. 计划,监视和执行批处理作业
2. 配置,启动和停止实时服务
3. 配置作业服务器,访问服务器和存储库使用情况
4. 配置和管理适配器
5. 管理用户
6. 通过Web服务发布批处理作业和实时服务
用户界面介绍
QQ截图20210318111958

Projects(项目):项目包含作业,工作流和数据流等子对象,项目是Data Services的最高级对象,主要用于组织管理子对象,一个项目通常包含多个作业

Job(作业):可独立安排执行的最小工作单元。 作业由工作流和数据流组成,这些工作流和数据流按顺序和指定方式处理流程。

Work flows(工作流):工作流指定处理数据流的顺序。将下级数据流安排在工作流下,以便一个数据流的输出准备好输入到预期数据流。

Data flows(数据流):数据流是将源数据转换为目标数据的过程。数据流按在工作流中排列的顺序处理数据。数据流定义了数据服务需要完成的基本任务。 基本任务是将数据从一个或多个源移动到一个或多个目标表或文件。通过标识提取数据的源,数据应进行的转换以及目标来定义数据流。

Data Services对象列表
20200811154500211
对象层级以及从属关系
20200811154704891
DS数据加载方式

将演示最基本的从其他系统中抽取数据到SAP HANA中(多图预警)

*注:如何创建datastores连接其他系统和数据库此文不进行阐述*,下面的演示为全量抽取,将一次将表中的全部数据抽取到你想要的数据库里。

根据对象层级以及从属关系来创建

也可以直接在local object library点击不同的对象独立创建(但不推荐,因为不好管理也不好找)

首先创建project,在local object library界面中选择project,然后空白处选择New

QQ截图20210318164049

起一个你喜欢的名字(project name)*见名知意,最好再加上注释🙏*,然后点击create(创建)

QQ截图20210318164227

之后再local object library中右击刚刚创建的project,点击open(打开)

QQ截图20210318164346

它就会在project Area上显示树状图

QQ截图20210318170041

之后右击空白位置或者project选择new batch job(新建批量作业)

QQ截图20210318170601

通常名字和要进行的业务相关 见名知意(这就是我们需要执行的作业,可以手动或者定时执行

QQ截图20210318170649

之后双击就应该会在右侧工作区打开,应该是一片空白,空无一物,这时我们就需要点击右侧工具区的Dataflow(数据流),然后到空白区域左键单击新建一个df,名字一般应和你要抽取的表同名,比如说:DF_XXXX

QQ截图20210318171011

之后鼠标双击你刚才新建的df进入df的工作区,准备开始抽取表数据

流程:
  1. 在你要抽取数据的datastore中右键tables—import by name创建source表(源数据表,也就是你要抽取的表)。
  2. 鼠标左键拖拽到df工作区,选择Make Source(作为源表) 。
  3. 然后在右侧工具区拖拽query对数据进行处理(也可以跳过query,不进行输入处理,直接将源表和目标表连线
  4. 从右侧工具区拖拽template(临时表)到df工具区,填上你想要抽到哪个datastore和schema。
  5. 保存执行
示例:

步骤一:

在datastore导入表

QQ截图20210319085415

填写表的信息,之后点击import导入

QQ截图20210319085849

步骤二:

鼠标拖拽刚刚导入的表到右侧ds工作区,选择make source作为源表

QQ截图20210319090259

最后是这个样子的,中间图示的表格深蓝色而且清晰,绿色箭头向右

QQ截图20210319090340

步骤三:

点击工具区中的query,鼠标图标应该会发生改变,随后点击df的工作区创建一个query

QQ截图20210319090825

之后通过鼠标悬浮到源表右侧的小点,鼠标图标应该会变换成一个拿着粉笔的手,随后左键单击拖拽就应该会出现连线,将线连接到query控件左侧的三角形

QQ截图20210319091413

随后双击进入query,对数据进行处理

QQ截图20210319091622

步骤四:

也是通过左键工具区中的template(临时表)到ds工作区,填写要抽到库里的表名和schema,表名应和要抽取的表同名

QQ截图20210319092829

随后通过query连线到临时表,最后应该是这样子的

QQ截图20210319093220

双击右侧的临时表,点击options,拖到最下方吧这一行改为YES!不然字符串的数据抽取可能出现问题

QQ截图20210319093541 QQ截图20210319093607

步骤五:

到job页面或者在左侧project area右键job选运行。如果没保存会提示让你保存,随后执行,点ok

QQ截图20210319094058 QQ截图20210319094206

保存执行!

本文中展示数据皆为示例,不太具有实际参考价值,但步骤相同,推荐举一反三。

部分引自SAP Data Services Designer - Ⅰ(User interface and Setting)

fdacab61d58425e368f30729feb2d2c8

この何もないの世界に、お前わ何が欲しいのが

开窗函数

排序函数

开窗函数over(),包含三个分析子句:分组(partition by), 排序(order by), 窗口(rows) ,他们的使用形式如下:over(partition by 排序字段 order by 分区字段 rows between 开窗规则)。

开窗函数
Oracle从8.1.6开始提供分析函数,分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行。

开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化,

示例数据:

QQ截图20210312144133

第一大类:聚合开窗函数====》聚合函数(列) OVER (选项)

:sum(),count()等都可以拼接

第二大类:排序开窗函数====》排序函数(列) OVER(选项)

:rank(),row_number()等需要和over一起使用排序函数

不限于此,很多函数都可以配合over(),在这里不做过多介绍

语法:

1
2
3
4
5
6
7
8
9
--select 函数名()over(partition by 需要分区的列名 order by 想排序的列名 rows/range 
--unbounded preceding and unbouned following针对当前所有记录的前一条、后一条记录,也就是表中的所有记录
--unbounded:不受控制的,无限的
--preceding:在...之前
--following:在...之后
-- BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) from 表名
--such as
select *,sum(sage)over(order by sage rows between UNBOUNDED preceding and UNBOUNDED following)
from "student"

分区(partition by ):

功能与group by几乎相同,但group by对于聚合的分组只显示一行数据,并且select后的字段如果其中一个被group by,那么其他字段要么是聚合函数要么也被group by分组聚合。

栗子:分别求男女同学年龄总和

QQ截图20210315093653

而聚合函数搭配over()可以一组返回多行数据,而且其他字段不需要为被分组或被聚合。

排序(order by):

栗子:男女分别根据年龄降序排列

QQ截图20210315100337

没什么需要多说的,这里使用了rank()函数排序,partition by相当于group by,order by还是原意。

注:关于rank()等排序函数请移步其他文章,本文不做说明

栗子ii:男女按照年龄降序分别求出年龄累计

QQ截图20210315135813

这里发生了变化,当使用sum()或count()配合over使用,并且使用partition by和order by,就会根据排序进行逐渐的累加或者求和,可以用于计算各个月份随时间累计数等情况

窗口(rows/range)注:rows可以使用,但是range没研究明白报错:

在Over子句中,使用Rows 或Range 进一步限制分区的数据行,在使用时,必须注意:

必需条件:如果使用Rows 或 Range必须跟在Order by 子句之后,对排序的结果进行限制;
Rows:使用固定的行数来限制分区中的数据行数量;The ROWS clause limits the rows within a partition by specifying a fixed number of rows preceding or following the current row.
Range:使用Value的范围来限制分区中的数据行数量,排序列的重复值,被认为是一个值;The RANGE clause logically limits the rows within a partition by specifying a range of values with respect to the value in the current row.
在分区中,如果排序行不存在重复值,Rows和Range返回的结果是相同的;如果排序行存储在重复值,Rows和Range返回的结果可能不同;
Range子句只能从分区的开始或结尾到当前行开始计算,不能使用 PRECEDING 和 FOLLOWING;
使用在Rows 和 Range子句中的特殊关键字:

UNBOUNDED PRECEDING:指定分区的第一行
UNBOUNDED FOLLOWING:指定分区的最后一行
CURRENT ROW:指定分区的当前数据行
PRECEDING:在分区中,指定当前行之前的数据行数量,UINT_Number是>=0的整数
FOLLOWING:在分区中,指定当前行之后的数据行数量,UINT_Number是>=0的整数

1
2
3
4
--每行数据都会被开窗影响,每行数据都会根据开窗的行数来向上或向下进行函数的操作。
--当上面没有行时上面的行不做影响,下面也没有行时同上。
select *,sum(sage)over(order by sage rows between 前多少行 preceding and 后多少行 following)
from "student"

栗子:对分区中的连续两行计算加和,将每行和下一行进行相加

QQ截图20210315150441

第一行=第一行sage+第二行sage,第二行=第二行sage+第三行sage………..以此类推

图中第五行数据和第十行数据都是分区中的最后一行数据,没有下面的行进行相加所以是原数

–栗子ii:对分区中的当前行向下计算加和,将每行到当前分区最底下的进行相加

Could not execute ‘select *,sum(sage)over(order by sage range between unbounded preceding and unbounded following) …’
SAP DBTech JDBC: [7]: feature not supported: Window frame specification of RANGE not allowed for this window function: line 1 col 10 (at pos 9)

202005241421367251716257711.500x0.jpg.webp

to be continued(多半是鸽了😜

排序函数

排序函数:

排序函数的作用是基于一个结果集返回一个排序值。排序值就是一个数字,这个数字是典型的以1开始且自增长为1的行值。由排序函数来决定排序值。可以使唯一的对于当前结果集,或者某些行数据有相同的排序值。接下来介绍不同的排序函数以及如何使用这些函数。

示例数据:

QQ截图20210312144133

语法:

1
2
3
4
--select 函数名()over(order by 想排序的列名) from 表名
--such as
select rank()over(partition by ssex order by sage desc)
from "student"

注:over()开窗函数用法在此文中不进行介绍

ROW_NUMBER()

定义:ROW_NUMBER()函数作用就是将select查询到的数据进行排序,每一条数据加一个序号,他不能用做于学生年龄的排名,一般多用于分页查询,因为在遇到相同年龄时序号依然会继续自增,而非并列的序号。

栗子:根据年龄降序排列学生

QQ截图20210312145034

ROW_NUMBER()当遇到相同年龄时序号依然会自增

RANK()

定义:RANK()函数,顾名思义排名函数,可以对某一个字段进行排名,与ROW_NUMBER()不同,当存在相同年龄的学生时,ROW_NUMBER()会继续进行排序,他们的序号不相同,而Rank()则不一样,当出现相同的年龄时,他们的排名是一样的,并且会跳过已经占用的序号。

栗子:根据年龄降序排列学生

QQ截图20210312145927

这里第五条数据突然从2号序号跳跃到5号序号,是因为上面已经有四位同学被排序过,按照已经被排序的数据条数就是5号

RANK()当遇到相同年龄时序号时序号不会自增,而是和相同年龄时的序号相同。同时当再次向下排序遇到不同年龄时,会根据已进行排序的条数来跳跃序号。

DENSE_RANK()

定义:DENSE_RANK()函数也是排名函数,和RANK()功能相似,也是对字段进行排名,那它和RANK()到底有什么不同那?

栗子:根据年龄降序排列学生

QQ截图20210312150624

dense_rank函数的功能与rank函数类似,dense_rank函数在生成序号时是连续的,而rank函数生成的序号有可能不连续。dense_rank函数出现相同排名时,将不跳过相同排名号,rank值紧接上一次的rank值。在各个分组内,rank()是跳跃排序,有两个第一名时接下来就是第四名,dense_rank()是连续排序,有两个第一名时仍然跟着第二名。

NTILE()

定义:ntile函数可以对序号进行分组处理,将有序分区中的行分发到指定数目的组中。 各个组有编号,编号从一开始。 对于每一个行,ntile 将返回此行所属的组的编号。这就相当于将查询出来的记录集放到指定长度的数组中,每一个数组元素存放一定数量的记录。ntile函数为每条记录生成的序号就是这条记录所有的数组元素的索引(从1开始)。也可以将每一个分配记录的数组元素称为“桶”。ntile函数有一个参数,用来指定桶数。

QQ截图20210312160137

将年龄降序并且平均分为四个桶(区)

  ntile函数的分组依据(约定):

  1、每组的记录数不能大于它上一组的记录数,即编号小的桶放的记录数不能小于编号大的桶。就是说,第1组中的记录数只能大于等于第2组及以后各组中的记录数。

  2、所有组中的记录数要么都相同,要么从某一个记录较少的组(命名为X)开始后面所有组的记录数都与该组(X组)的记录数相同。也就是说,如果有个组,前三组的记录数都是9,而第四组的记录数是8,那么第五组和第六组的记录数也必须是8。

以上为四个排序函数的用法,最后一个可能不算排序,而是根据排序内容分桶,应用场景一般不为排序。而这些排序函数都依赖于over分窗函数,可以看到排序都是在它之中进行的,同时它也可以使用partition by排序的同时分组,类似于group by,也可以根据row和range进行开窗,但这里不过多解释。!

总结:

在使用排名函数的时候需要注意以下三点:

  1、排名函数必须有 OVER 子句。

  2、排名函数必须有包含 ORDER BY 的 OVER 子句。

  3、分组内从1开始排序。

引自[Sql 四大排名函数(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介]

20200429013947.52ff80ba8cacf5780b8e9e93e02e02ef

wHo i'M I

我的生活是一个圆,是一个泡沫,而我在泡沫的中间。

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment