char vs varchar vs nvarchar

分享
char vs varchar vs nvarchar

MSSQL Server

char(n) vs varchar(n)

  • n为字节单位而非字符单位,这是有区别的,在单字节编码字符集存储时,n的大小即为字符长度,当所存储的字符无法使用单字节编码字符集表示时则会采用双字节字符集存储,n的大小则是字符的2倍
  • char会存储固定字节长度的字符,如果定义char(4)则插入'ab'会被存储为'ab ',超出字节长度的字符串会被截断。实际存储大小总是4 bytes,另外插入'ab '后查询出的结果是会去除末尾空格为'ab'
  • varchar存储可变字节长度的字符,如果定义varchar(4)则插入'ab'即存储为'ab'而不会向char一样使用空格填充,在检索时也不会去除末尾空格。然而使用varchar(n)存储的字符总是会比真实大小大2 bytes,因为varchar类型总是会使用2 bytes来存储字节长度,2 bytes最大可表示65535‬个字节长度,而varchar(n)最大为1~8000 bytes,所以2 bytes足够存储最大字节长度
插入值 char(4) 实际存储大小 varchar(4) 实际存储大小
'' ' ' 4 bytes '' 0+2 bytes
'ab' 'ab ' 4 bytes 'ab' 2+2 bytes
'abcd' 'abcd' 4 bytes 'abcd' 4+2 bytes
'abcdefg' 'abcd' 4 bytes 'abcd' 4+2 bytes

[var]char(n) vs n[var]char(n)

  • 很多人或许会对char/ncharvarchar/nvarchar之间的区别感到困惑,这不过是不同的编码方式带来的差异
  • 带有n前缀的表示使用Unicode标准以及UTF-16编码方式(适用于开启了补充字符(SC)的排序规的情况下,如果未开启,则使用UCS-2编码方式),前者规定了每个字符对应的代码点(Code Point)是多少,后者表示如何对Unicode定义的代码点进行编码,由于UTF-16BMP区域使用2 bytes进行编码,超出BMP区域的则使用4 bytes编码,所以nvarchar实际存储大小需要根据具体的值来确定,例如存储'a'则占用大小为2 bytes,存储一个emoji表情则可能占用4 bytes
  • 对于[var]char,则使用对应排序规则的代码页进行编码,这意味着不同区域无法通用存储的编码字符。不过从SQL Server 2019(15.x)开始,如果开启了UTF-8排序规则,则使用UTF-8进行编码,与UTF-16一样能够表示Unicode标准所定义的字符集。UTF-8对于字符的编码长度也是可变的,例如ASCII码表所定义的字符则只需要1 byte,对于一个非BMP区域的字符则可能需要最大4 bytes进行编码

下表所示的varchar使用UTF-8排序规则

插入值 varchar(50) 实际存储大小 nvarchar(50) 实际存储大小
'' '' 0+2 bytes '' 0+2 bytes
'a' 'a' 1+2 bytes 'a' 2+2 bytes
'abcd' 'abcd' 4+2 bytes 'abcd' 8+2 bytes
'😀' '😀' 4+2 bytes '😀' 4+2 bytes

MySQL

char(n) vs varchar(n)

相比较MSSQL ServerMySQL中的n则很好理解:无论采用哪种编码方式,它始终表示字符串的长度。在MySQL中,varchar类型也有一个长度值,然而与MSSQL Server所不同的是,它的存储长度值并不总是2 bytes,而是存储字节超过255 bytes时才使用两个字节存储

插入值 char(4) 实际存储大小 varchar(4) 实际存储大小
'' ' ' 4 bytes '' 0+1 bytes
'ab' 'ab ' 4 bytes 'ab' 2+1 bytes
'abcd' 'abcd' 4 bytes 'abcd' 4+1 bytes
'abcdefg' 'abcd' 4 bytes 'abcd' 4+1 bytes

Reference

阅读更多

以太坊黑暗森林-抢跑(front running)

以太坊黑暗森林-抢跑(front running)

前言 鸽了很久之后的今天突然心血来潮,准备写一个系列:以太坊黑暗森林,它介绍以太坊生态上的各种奇思妙想和逆天的攻击方式,会从简单的、常见的攻击方式开始介绍。取这个名字是因为我接触以太坊不久后看的一篇文章 Ethereum is a Dark Forest ,让我想起了《三体》小说中刘慈欣描述的黑暗森林,以太坊是一个弱肉强食的、没有规则的世界,猎人们总是躲在背后监听所有的交易,一旦发现猎物,它们会把它的血给吸干。 开盘抢币 相信进入以太坊生态的韭菜们,一定有过在 uniswap 上买刚开盘新币的经历,新开盘的币,一般会上涨几倍甚至十几倍,越早买入则越能低价买入。你守着时间,等着项目方添加流动性后第一时间买入代币,但是你发现,无论你的手速多块,总是看到一开盘,价格已经飚了几倍,你骂骂咧咧,开始不断拉高 gas 费用,尝试继续买入,但是你眼睁睁的看着代币涨到十倍,自己的交易却一直失败,你开始怀疑项目方自己抢跑,怀疑项目方捣鬼:肯定是项目方吃相难看,用老鼠仓提前买了。另一些聪明人,研究了以太坊的基本技术,他们在 ethscan

By FatTiger
C#:IDisposable 和 析构函数

C#:IDisposable 和 析构函数

C# 中有两种释放资源的方式:实现 IDisposable 或使用析构函数。通常,必须在特定时间释放资源的场景中,我们实现 IDisposable,像这样: public class ExampleDispose : IDisposable { // 非托管资源 private IntPtr _handle; // 使用的其它托管资源 private readonly Stream _stream; private bool disposed = false; public ExampleDispose(Stream stream, IntPtr handle) { this._stream = stream; this._handle = handle; } public void Dispose() { if (disposed) { return; } disposed = t

By FatTiger
ThreadLocal引发的灾难

ThreadLocal引发的灾难

在 Java 里有个称之为线程本地变量的类型叫做 ThreadLocal,它与 ThreadLocal 之于 C# 中是一样的作用,可以在线程范围内设置变量,这个变量只会在当前线程可被访问,但是它们有一点不同的是,在 Java 中,当你设置好变量后,在线程使用完毕回到线程池之前,需要手动调用 ThreadLocal.remove() 方法去清除线程本地变量,否则变量随着线程回到线程池,并且在下次使用此线程时此变量继续存在,而在 C# 中,线程回到线程池时会自动清除本地变量,因此无需手动去清除。 我们的业务有这样一个场景:某个业务 UserService 类中,具有多个方法会频繁(甚至循环)调用一个获取用户标签的接口,具体原因是因为某些方法会进行递归,数据结构有个树状结构,因此,为了优化接口响应时间以及看起来不那么蠢,我使用 ThreadLocal 将用户标签接口的返回数据存储到当前线程,因为在单个请求中,多次调用此接口获取数据是不必要的,它看起来像这样: /** * 此静态变量ThreadLocal会为每个线程创建本地副本, 因此USER_TAGS_THREAD_

By FatTiger
我在币安智能链的日子-区块链基础

我在币安智能链的日子-区块链基础

区块和链 无论是比特币还是以太坊,都是具有一个个区块(称之为Block)的链式结构,学过<数据结构>的肯定明白链表,区块链就像一个链表,每个区块都存储上一个区块哈希。 链(称之为Chain),有非常多的链,他们的协议不同,技术也不尽相同,比特币网络是一个链,以太坊网络是另一个链,每个链都有自己的目标(甚至目标只是为了圈钱),每个链也都有自己的代币,比特币网络的代币是比特币,每次交易都需要比特币作为手续费,以太坊网络代币是以太币,每次在以太坊网络的交易都需要以太币作为手续费。所以,链实际上作为基础设施,非常多的团队喜欢创建新的链,但是一个链光有网络光有代币不行,没有生态,很难成功。 币安智能链(Binance Smart Chain:BSC) 我的主要操作都是在BSC上,没有其它原因,只因为一个穷字。在BTC网络交易,需要BTC用作手续费,这个我可用不起,在以太坊(Ethereum)网络交易,需要以太币(ETH)作为用作手续费,按照以太币目前(

By FatTiger