0%

命令

1
2
sqlcmd -S .\SqlExpress
NET START/PAUSE/CONTINUE/STOP/ MSSQLSERVER

QQs未能成功实践。。
或者,任务管理器手动启动 MSSQL$SQLEXPRESS

schema

在MySQL中schema的概念和database一致

但是微软搞什么都要多加点概念,sqlserver中,表名前带有schema标记如dbo.table1,这里的dbo指数据库的默认用户database owner

导出表结构(create table)语句时会带着schema

1
2
3
4
5
6
7
8
create table [ent].[tabletemp](
[Id] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](50) NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

迁移时执行该语句会提示”The specified schema name “env” either does not exist or you do not have permission to use it.”

创建schema
1
create schema ent

变更schema
1
ALTER SCHEMA ent TRANSFER OBJECT::dbo.table1;  

创建新用户及授权访问

参考原文

  • 配置登录名 Database Server —> Security —> Logins —> 右键New Login
  • 常规General标签页中,配置认证方式等
  • 服务器角色(Server Roles)添加 public sysadmon
  • 用户映射(User Mapping)添加创建的新用户
  • 安全对象(Securable)搜索 —> 选择 The Server(当前数据库服务器名)
  • 状态默认

ServerName

sqlserver实例默认以计算机名+服务提供者命名,如SHAL400/SQLEXPRESS, 甚至用ip代替计算机名都会导致无法连接.

配置sqlserver支持远程访问:

  1. 从本地SSMS连接数据库,右键服务器—Facets—Server Configuration—RemoteAccessEnable=true
    sqlserver_remote_access
  2. 打开SQL Server Configuration Manager(SSCM) SQL Server Browser Running,
  3. SSCM—SQL Server Network Configuration—Protocols for SQLEXPRESS—TCP/IP Enable, 然后右键打开Properties设置ip及端口如下(注意IPAll的TCP Dynamic Ports不要写死)
    sqlserver_remote_access_tcpip
  4. 配置防火墙略

调用存储过程

1
EXEC storedProcedure1 @param='01'

约束Constraint

1
2
3
4
5
6
7
8
9
CREATE TABLE [dbo].[Group](
[ID] [uniqueidentifier] NOT NULL,
[CreateTime] [datetime2](7) NULL,
[Name] [nvarchar](80) NULL,
[Valid] [bit] NOT NULL,
[UpdateTime] [datetime2](7) NULL,
[Comment] [nvarchar](500) NULL,
CONSTRAINT [AK_Group_Name] UNIQUE ([Name])
) ON [PRIMARY]

关于大小写

据说sqlserver 安装过程中有是否区分大小写的选项,默认情况下无论表名、列名、字段、参数都不区分大小写,更过分的是查询条件的值也不区分————where name=’abc’和where name=’AbC’是一样的结果。如果要区分查询条件的大小写,中文网络上建议如下例子,追加条件

1
select * from table1 where name='abc' collate Chinese_PRC_CS_AI_WS 

Chinese_PRC_CS_AI_WS实际表示中国大陆UNICODE字符集规则(Chinese PRC),区分大小写(Case Sensitive,CS),不区分重音(Accent Insensitive,AI),区分宽度(Width Sensitive,WS,半角/全角字符受此条件影响)

类似的还有
1
2
SQL_Latin1_General_CP1_CS_AI
Latin1_General_CS_AI

查询当前默认规则
1
SELECT SERVERPROPERTY(N'Collation')

查询支持的字符集规则
1
SELECT * from ::fn_helpcollations()

内置对象的表

  • sys.schemas

  • 执行历史

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    SELECT TOP 1000 QS.creation_time, 
    SUBSTRING(ST.text,
    (QS.statement_start_offset / 2) + 1,
    ((CASE QS.statement_end_offset
    WHEN - 1 THEN DATALENGTH(st.text)
    ELSE QS.statement_end_offset
    END - QS.statement_start_offset) / 2) + 1)
    AS statement_text,
    ST.text,
    QS.total_worker_time,
    QS.last_worker_time,
    QS.max_worker_time,
    QS.min_worker_time
    FROM
    sys.dm_exec_query_stats QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) ST
    WHERE 1=1

    edit data

    SSIS提供了Edit Top 200 Rows,但是写入表格内容各种格式不正确,宜Script Table to…Insert to

    Guid用NEWID(),时间就用SYSDATETIME()

    STUFF

1
STUFF ( character_expression , start , length , character_expression )

CAST & CONVERT

数据类型转换

1
2
SELECT CAST(t1.num AS varchar) from t1;
SELECT CONVERT(varchar, t1.num) from t1;

将自然键替换为人工键

原实体以序列号为主键,现添加ID列并填充GUID

1
2
3
ALTER TABLE dbo.Table1 DROP CONSTRAINT PK_Table1 // 移除原主键
ALTER TABLE dbo.Table1 DROP COLUMN SerialNumber // 移除列
ALTER TABLE dbo.Table1 ADD ID uniqueidentifier NOT NULL default newID()

exception The object ‘DFTable1ID__34C8D9D1’ is dependent on column ‘ID’. ALTER TABLE DROP COLUMN failed because one or more objects access this column

ID作为列名会默认添加CONSTRAINT,如上所提及的DFTable1ID34C8D9D1 因此要删除这个ID列需要先 ALTER TABLE dbo.Table1 DROP CONSTRAINT DFTable1ID34C8D9D1

层次结构数据

具有父级、子级关系的层次结构数据
Oracle的递归查询语法:

1
select  * from t_dw CONNECT BY PRIOR id = parentID START WITH id='dw001'

SqlServer中没有上述语法,而使用内置hierarchyid简化层次结构数据的存储和查询,

https://www.meziantou.net/using-hierarchyid-with-entity-framework-core.htm

1
2
3
4
5
6
7
-- 根节点 /
update t_dw set orgLvl=HierarchyID::GetRoot() where parentID is null
-- 子树 /1/,/2/
update t_dw set orgLvl=HierarchyID::Parse('/1/') where name='dw1'
update t_dw set orgLvl=HierarchyID::Parse('/2/') where name='dw2'
-- 叶 /1/3/
update t_dw set orgLvl=HierarchyID::Parse('/1/1/') where name='dw1-a'

插入

1
2
3
insert t_dw (id,name,ParentID,orgLvl) 
values(newid(),'dw1-b','xxxxxxxxxxxxxxx',
HierarchyID::Parse('/1/').GetDescendant(CAST('/1/1/' AS hierarchyid), NULL))

得到/1/2/ dw1-b 即在/1/的子节点,左树为/1/1/右树为null位置插入新节点

层级

1
SELECT CAST('/1/2/' AS hierarchyid).GetLevel() -- 结果:2

后代
1
2
3
SELECT name, orgLvl.ToString()
FROM t_dw
WHERE orgLvl.IsDescendantOf(CAST('/1/' AS hierarchyid)) = 1

IsDescendant为1(表示true)返回所有后代(实际上也包括‘/1/’自己), 0返回所有非后代(父代,sibling树)
1
2
3
SELECT name, orgLvl.ToString()
FROM t_dw
WHERE orgLvl.GetAncestor(2) = HierarchyID::Parse('/1/')

GetAncestor返回指定层级的后代,参数为层级:0返回‘/1/’自己;1返回所有子节点,2返回所有孙子节点

移动

1
2
3
4
5
6
7
8
DECLARE @CurrentNode hierarchyid , @OldParent hierarchyid, @NewParent hierarchyid 
select @CurrentNode=orgLvl from t_dw where name='dw_x'; -- /1/1/
select @OldParent=orgLvl from t_dw where name='dw_old'; -- /1/
select @NewParent=orgLvl from t_dw where name='dw_new'; -- /3/
UPDATE t_dw
SET OrgNode = @CurrentNode.GetReparentedValue(@OldParent, @NewParent)
WHERE OrgNode = @CurrentNode ; -- /3/1/
GO

其他进阶操作:
查找祖先
列出祖先)
移动子树

获取每个表的数据条数

1
2
3
select schema_name(t.schema_id) as [Schema], t.name as TableName,i.rows as [RowCount] 
from sys.tables as t, sysindexes as i
where t.object_id = i.id and i.indid <=1

按rownumber删除

1
2
3
4
; with cte(rownum)as(
select row_number () over(partition by [Col1], [Col2] order by Col3) from [table]
)
delete from cte where rownum > 1

按Col1 Col2分组删除 保留组唯一

login fail Error 18456

CSDN Blog:SQL Server Error 18456

other issues: Microsoft Docs: Troubleshooting Connect to SQL Server

查看版本

1
select @@version

作业和代理

某需求欲使用SQL Server的计划进行自动备份,启动SQL Server Agent时账户密码不正确 且该账号登录SSMS没有计划、代理等菜单

SQL Server Express没有这部分功能

sqlcmd

关于ORM

参考阮一峰《ORM 实例教程》:
面向对象编程(我们的后台)把所以实体看成对象(即Object),关系型数据库则是采用实体之间的关系(即Relation)连接数据,打通后台对象和关系数据库之间的关系而做的映射(Mapping)就是ORM。或者说ORM是通过对象实例语法,完成关系型数据库操作的技术。

举个栗子

1
2
3
SELECT id, first_name, last_name, phone, birth_date, sex
FROM persons
WHERE id = 10

编程调用SQL的写法大致是
1
2
res = db.execSql(sql);
name = res[0]["FIRST_NAME"];

ORM要实现的写法是
1
2
p = Person.get(10);
name = p.first_name;

优点:

  • 数据模型都在一个地方定义,更容易更新和维护,也利于重用代码。
  • ORM 有现成的工具,很多功能都可以自动完成,比如数据消毒、预处理、事务等等。
  • 它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
  • 基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
  • 你不必编写性能不佳的 SQL。

缺点

  • ORM 库不是轻量级工具,往往需要花很多精力学习和设置。
  • 对于复杂的查询,ORM 要么是无法表达,要么是性能不如原生的 SQL。
  • ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。

(删除线部分QQs不是特别赞同)

同属ORM技术的比如 Hibernate, MyBatis

关于ODBC

开放式数据库连接(Open Database Connection),连接数据库进行查询的规范(specification)

entity framwork,dbcontext, datarepository

Include, ThenInclude:

1
2
3
result = await _context.employees.Where(employee => employee.level == highlvl)
.Include(employee => employee.department)
.ThenInclude(department => department.bills).ToListAsync();

多对多关系会造成循环引用(circular reference),默认情况下序列化类会报异常,参考Add mechanism to handle circular references when serializing

Dapper.net

据说vsftp是very safe FTP, vsftp服务以ssl保护数据传输,使用22端口而不是21端口。

1
sudo apt-get install vsftpd

参数配置:/etc/vsftpd.conf,用cp命令备份下先
参数定义—>here

修改配置后重启

1
service vsftpd restart

FileZilla

FastDFS是国人大神开发的用于小文件(<500Mb)存储的分布式文件管理系统

Github: happyfish100/fastdfs

戳—>详细配置步骤,超级详细,傻瓜式,环境是Centos。对于ubuntu,安装编译环境的方式会有不同

centos编译环境

1
2
yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vi -y

ubuntu编译环境

1
apt install git gcc g++ make automake autoconf libtool libpcre3 libpcre3-dev zlib1g zlib1g-dev  libssl-dev wget vi

tracker和storage

fdfs
如配置步骤所述,fdfs安装好后有tracker配置文件和storage配置文件,前者配置tracker用于上传下载的调度,后者配置storage作为文件存储。

tracher监听storage的状态同步消息,使当client上传或下载时,提供可用的storage路径

storage可以配置为group,相同group的文件会相互拷贝(这个是需要一定时间的,在集群方案中需要考虑)

上传文件

上传
为使业务应用服务器实现上传fDFS,应实现client功能,即

  • 请求tracker 获取可用storage的 ip port等
  • 调用相应的storage接口上传文件,接受返回的file_id信息

下载文件

下载
如图,client实现下载,需

  • 请求tracker 获拉取下载的storage的 ip port等
  • 调用相应的storage接口下载文件

变量和常量

a. 用var关键字来定义变量,使用$来引用变量。

b. 寄存器变量 $0~$9,$R0~$R9

c. 系统预置变量

  • $INSTDIR
    用户定义的解压路径。
  • $PROGRAMFILES
    程序文件目录(通常为 C:\Program Files 但是运行时会检测)。
  • $COMMONFILES
    公用文件目录。这是应用程序共享组件的目录(通常为 C:\Program Files\Common Files 但是运行时会检测)。
  • $DESKTOP
    Windows 桌面目录(通常为 C:\windows\desktop 但是运行时会检测)。该常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
  • $EXEDIR
    安装程序运行时的位置。(从技术上来说你可以修改改变量,但并不是一个好方法)。
  • ${NSISDIR}
    包含 NSIS 安装目录的一个标记。在编译时会检测到。常用于在你想调用一个在 NSIS 目录下的资源时,例如:图标、界面……
  • $WINDIR
    Windows 目录(通常为 C:\windows 或 C:\winnt 但在运行时会检测)
  • $SYSDIR
    Windows 系统目录(通常为 C:\windows\system 或 C:\winnt\system32 但在运行时会检测)
  • $TEMP
    系统临时目录(通常为 C:\windows\temp 但在运行时会检测)
  • $STARTMENU
    开始菜单目录(常用于添加一个开始菜单项,使用 CreateShortCut)。该常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
  • $SMPROGRAMS
    开始菜单程序目录(当你想定位 + $STARTMENU\程序 时可以使用它)。该常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
  • $SMSTARTUP
    开始菜单程序/启动 目录。该常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
  • $QUICKLAUNCH
    在 IE4 活动桌面及以上的快速启动目录。如果快速启动不可用,仅仅返回和 + $TEMP 一样。
  • $DOCUMENTS
    文档目录。一个当前用户典型的路径形如 C:\Documents and Settings\Foo\My Documents。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量在 Windows 95 且 Internet Explorer 4 没有安装时无效。
  • $SENDTO
    该目录包含了“发送到”菜单快捷项。
  • $RECENT
    该目录包含了指向用户最近文档的快捷方式。
  • $FAVORITES
    该目录包含了指向用户网络收藏夹、文档等的快捷方式。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量在 Windows 95 且 Internet Explorer 4 没有安装时无效。
  • $MUSIC
    用户的音乐文件目录。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量仅在 Windows XP、ME 及以上才有效。
  • $PICTURES
    用户的图片目录。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量仅在 Windows 2000、XP、ME 及以上才有效。
  • $VIDEOS
    用户的视频文件目录。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量仅在 Windows XP、ME 及以上才有效。
  • $NETHOOD
    该目录包含了可能存在于我的网络位置、网上邻居文件夹的链接对象。
    该常量在 Windows 95 且 Internet Explorer 4 和活动桌面没有安装时无效。
  • $FONTS
    系统字体目录。
  • $TEMPLATES
    文档模板目录。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
  • $APPDATA
    应用程序数据目录。当前用户路径的检测需要 Internet Explorer 4 及以上。所有用户路径的检测需要 Internet Explorer 5 及以上。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量在 Windows 95 且 Internet Explorer 4 和活动桌面没有安装时无效。
  • $PRINTHOOD
    该目录包含了可能存在于打印机文件夹的链接对象。
    该常量在 Windows 95 和 Windows 98 上无效。
  • $INTERNET_CACHE
    Internet Explorer 的临时文件目录。
    该常量在 Windows 95 和 Windows NT 且 Internet Explorer 4 和活动桌面没有安装时无效。
  • $COOKIES
    Internet Explorer 的 Cookies 目录。
    该常量在 Windows 95 和 Windows NT 且 Internet Explorer 4 和活动桌面没有安装时无效。
  • $HISTORY
    Internet Explorer 的历史记录目录。
    该常量在 Windows 95 和 Windows NT 且 Internet Explorer 4 和活动桌面没有安装时无效。
  • $PROFILE
    用户的个人配置目录。一个典型的路径如 C:\Documents and Settings\Foo。
    该常量在 Windows 2000 及以上有效。
  • $ADMINTOOLS
    一个保存管理工具的目录。这个常量的内容(所有用户或当前用户)取决于 SetShellVarContext 设置。默认为当前用户。
    该常量在 Windows 2000、ME 及以上有效。
  • $RESOURCES
    该资源目录保存了主题和其他 Windows 资源(通常为 C:\Windows\Resources 但在运行时会检测)。
    该常量在 Windows XP 及以上有效。
  • $RESOURCES_LOCALIZED
    该本地的资源目录保存了主题和其他 Windows 资源(通常为 C:\Windows\Resources\1033 但在运行时会检测)。
    该常量在 Windows XP 及以上有效。
  • $CDBURN_AREA
    一个在烧录 CD 时储存文件的目录。.
    该常量在 Windows XP 及以上有效。
  • $HWNDPARENT
    父窗口的十进制 HWND。
  • $PLUGINSDIR
    该路径是一个临时目录,当第一次使用一个插件或一个调用 InitPluginsDir 时被创建。该文件夹当解压包退出时会被自动删除。这个文件夹的用意是用来保存给 InstallOptions 使用的 INI 文件、启动画面位图或其他插件运行需要的文件。

函数

NSIS函数本身没有输出输出,但是可以访问变量和堆栈

使用Pop Push实现输入输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Function LCIDtoTAG 
${case} "1036"
Push "fr_FR"
${break}
${case} "1049"
Push "ru_RU"
${break}
${case} "1041"
Push "ja_JP"
${break}
${case} "2052"
Push "zh_CN"
${break}
${default}
Push "en_US"
${break}
${endswitch}
FunctionEnd
Section
Call LCIDtoTAG
Pop $0
SectionEnd

跳转

1
2
3
4
StrCmp $1 "" +1 +2
DetailPrint "parameter is empty"

DetailPrint "parameter is not empty"

+n是从该语句向下偏移的“指针”,空行过滤掉不计入偏移量

—> NSIS收录插件 https://nsis.sourceforge.io/Category:Plugins

—> 插件本体是托管dll文件,区分unicode编码和ansi编码

—> NSIS默认检索在NSIS代码目录下的Plugins文件夹,作为插件路径,或可使用标识符!addplugindir 指定其他目录

一个栗子

1
2
3
4
5
6
7
8
9
!ifndef TARGETDIR
!ifdef NSIS_UNICODE
!define TARGETDIR "..\binU"
!else
!define TARGETDIR "..\bin"
!endif
!endif

!addplugindir "${TARGETDIR}"

曾将!ifdef误解为if not define,NSIS用!xxx做关键字真的是坑。

NGX-Translate is an internationalization library for Angular. NGX-Translate is also extremely modular. It is written in a way that makes it really easy to replace any part with a custom implementation in case the existing one doesn’t fit your needs.

Why ngx-translate exists if we already have built-in Angular i18n

关于载入翻译文件

集成步骤

1
npm install @ngx-translate/core @ngx-translate/http-loader --save

在模块中引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {TranslateModule} from '@ngx-translate/core';

export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient]
},
defaultLanguage: 'en'
})
],
bootstrap: [AppComponent]
})
export class AppModule { }

组件注入及初始化
1
2
3
4
5
6
7
8
export class AppComponent {
constructor(translate: TranslateService) {
// this language will be used as a fallback when a translation isn't found in the current language
translate.setDefaultLang('en');
// the lang to use, if the lang isn't available, it will use the current loader to get them
translate.use('en');
}
}

在模板中使用管道标记待翻译标记
1
2
3
4
5
<p>{{"sayHi" | translate}}</p>

<p>{{'sayHiWithParameter' | translate:userObj}}</p>

<p>{{ 'ROLES.' + role | uppercase | translate }}</p>

翻译文件,对于HTTP Loader是以Locale_ID命名的Json
1
2
3
4
5
6
src
├───assets
│ ├───i18n
│ │ en.json
│ │ fr.json
│ │ zh.json

翻译格式
1
2
3
4
5
6
7
8
{
"introductionHeader":"你好",
"ROLE":{
"ADMIN": "管理员",
"USER": "用户"
},
"sayHiWithParameter":"你好,{{pride}}的{{lastname}}}先生"
}

在ts代码中引用翻译
1
2
3
4
translate.get('HELLO', {value: 'world'}).subscribe((res: string) => {
console.log(res);
//=> 'hello world'
});

使用 PO Loader

官方提供给的 @biesbjerg/ngx-translate-po-http-loader,很遗憾,并不好使,或许是因为新版本的gettext-parser将msgctxt(信息上下文)作为一级父节点,而ngx-translate-po-http-loader一直没有更新使支持msgctxt

部分zh_CN.po文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Language: zh_CN\n"
"X-Qt-Contexts: true\n"

#: ../scanflow/res/qml/Dialog/AboutDialog.qml:25
msgctxt "AboutDialog|"
msgid "About"
msgstr "关于"

#: ../scanflow/res/qml/Dialog/AboutDialog.qml:52
msgctxt "AboutDialog|"
msgid "Product Version"
msgstr "产品版本"

通过上文“翻译格式”可知,ngx-translate允许多级属性mapping,如
1
<h1>{{ "AboutDialog|.About" |translate}}</h1>

另外,gettext-parser会将分段的msgid和msgstr进行合并,即
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
msgctxt "ErrorMessage|"
msgid ""
"Application preferences have been damaged. Reinstall the application to "
"solve the problem."
msgstr ""
"Předvolby aplikace byly poškozeny. Problém vyřešíte opětovnou instalací "
"aplikace."
====>
{
...
"ErrorMessage|":{
msgctxt:"ErrorMessage|",
msgid:"Application preferences have been damaged. Reinstall the application to solve the problem.",
msgstr:["Předvolby aplikace byly poškozeny. Problém vyřešíte opětovnou instalací aplikace."]
}
}

原理见@ngx-translate/core/fesm2015/ngx-translate-core.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
getValue(target, key) {
/** @type {?} */
let keys = typeof key === 'string' ? key.split('.') : [key];
key = '';
do {
key += keys.shift();
if (isDefined(target) && isDefined(target[key]) && (typeof target[key] === 'object' || !keys.length)) {
target = target[key];
key = '';
}
else if (!keys.length) {
target = undefined;
}
else {
key += '.';
}
} while (keys.length);
return target;
}

结合ngx-translate解析翻译时的逻辑,在实现PO Loader的getTranslation方法(关于自定义loader的继承和实现,ngx-translate有指引 -> link)时,应将msgctxt,msgid转为父子属性形式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public getTranslation(lang: string): Observable<any> {
return this._http
.get(`${this._prefix}/${lang}${this._suffix}`, { responseType: 'text' })
.pipe(
map((contents: string) => this.parse(contents)));
}

/**
* Parse po file
* @param contents
* @returns {any}
*/
public parse(contents: string): any {
let translations: { [key: string]: object | string } = {};

const po = gettext.po.parse(contents, 'utf-8');
if (!po.translations.hasOwnProperty(this.domain)) {
return translations;
}

Object.keys(po.translations)
.forEach(domain => {
if (domain.length === 0) { return; }
if (po.translations[domain].msgstr) { // there is no msgctxt
translations[domain] = po.translations[domain].msgstr;
} else {
// context
translations[domain] = {};
Object.keys(po.translations[domain]).forEach(key => {
let translation: string | Array<string> = po.translations[domain][key].msgstr.pop();
if (translation instanceof Array && translation.length > 0) {
translation = translation[0];
}
if (key.length > 0 && translation.length > 0) {
translations[domain][key] = translation;
}
});
}
});
return translations;
}

引入自己的 loader module
1
2
3
import { TranslatePoHttpLoader } from './edited-po-loader';
export function createTranslateLoader(http: HttpClient) {
return new TranslatePoHttpLoader(http, 'assets/i18n', '.po');

缺失的翻译 handle missing translations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {MissingTranslationHandler, MissingTranslationHandlerParams} from '@ngx-translate/core';

export class MyMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
return 'some value';
}
}
@NgModule({
imports: [
BrowserModule,
TranslateModule.forRoot({
missingTranslationHandler: {provide: MissingTranslationHandler, useClass: MyMissingTranslationHandler},
useDefaultLang: false
})
],
providers: [

],
bootstrap: [AppComponent]
})
export class AppModule { }

plural and select

这个库没有相应的api与i18n的 plural / select 模式相对应,对于这些场景需要使用ngIf或ngSwitch指令实现

ng build, ng serve是JIT, ng build —aot, ng build —prod, ng serve —aot是AOT 从Angular 9开始,默认情况下,对于提前编译器,编译选项设置为true。

JIT(Just in Time)由浏览器将源码编译成js执行,QQs:浏览器居然可以编译代码!
AOT(Ahead of Time)先编译成可执行的js,再交给浏览器

The Angular ahead-of-time (AOT) compiler converts Angular HTML and TypeScript code into efficient JavaScript code during the build phase, before the browser downloads and runs that code. This is the best compilation mode for production environments, with decreased load time and increased performance compared to just-in-time (JIT) compilation.AOT 编译器在浏览器下载并运行之前,将Angular HTML、 ts代码转为es5代码,是生产环境的最佳实践,相比JIT更能缩短加载时间并提高性能
(aot会根据angular.json 的配置生成到/dist之类的目录) 在angular.json中配置build命令的选项,包括生成目录等

what is aot and jit compiler in angular

Li Mei’s Blog: Angular深入理解编译机制
采取两种编译方式,注意修改angular.json architect.build.option.aot改为false,比较不使用—aot和使用时生成js的内容(见main.js, vendor.js是包含compiler等工具链的源码),可见生成的js包含angular模板语法,只是ts编译成了es5

另,使用source-map-explorer工具分析编译生成的js文件

1
npx source-map-explorer dist/main.js --no-border-checks

Angular Doc: AOT工作原理
YouTube: ng-conf
Angular编译机制(AOT、JIT)
AOT和ngc
ngc是专用于Angular项目的tsc替代者。它内部封装了tsc,还额外增加了用于Angular的选项、输出额外的文件。配置见于tsconfig.json, tsc读取tsconfig配置文件的compilerOptions部分,ngc读取angularCompilerOptions部分。

to be continue

数据层应用程序 (DAC) 是一个逻辑数据库管理实体,用于定义与用户数据库关联的所有 SQL Server 对象,如表、视图和实例对象(包括登录名)。 DAC 是 SQL Server 数据库部署的一个自包含单元,它使数据层开发人员和数据库管理员能够将 SQL Server 对象打包到一个名为“DAC 包”(也称作 DACPAC)的可移植项目中。

Transact-SQL(又称T-SQL),是在Microsoft SQL Server和Sybase SQL Server上的ANSI SQL实现,与Oracle的PL/SQL性质相近(不只是实现ANSI SQL,也为自身数据库系统的特性提供实现支持),当前在Microsoft SQL Server和Sybase Adaptive Server中仍然被使用为核心的查询语言。

下文涉及的函数/方法限T-SQL使用,在MySQL和Oracle中未必兼容

存储过程的查询条件

待改进的一种条件拼接:

1
2
3
SELECT * FROM LocalExport where 
SSO=IIF(@SSO is null, SSO, @SSO)
and SN=IIF(@SN is null, SN, @SN)

T-SQL方法
IIF(expression, return value when ture, return value when false)

待改进是因为存在下述bug:当缺省SN过滤条件(即@SN为null)时,记录中SN列的值为空的行不会查出,即null=null为false

可以这么表达
1
2
3
SELECT * from LocalExport where 
(ISNULL(@SSO, '')='' OR SSO=@SSO)
and (ISNULL(@SN, '')='' OR SN=@SN)

动态SQL语句
1
2
3
4
5
6
7
8
9
10
SET @SQL='select * from LocalExport where 1=1';
IF @SSO is not null
BEGIN
SET @SQL=@SQL+' AND SSO=@SSO'
END
IF @SN is not null
BEGIN
SET @SQL=@SQL+' AND SN=@SN'
END
EXEC sp_executesql @SQL