【手记】小心在where中使用NEWID()的大坑 【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题 【C#】组件分享:FormDragger窗体拖拽器 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

时间:2023-03-08 23:18:20
【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

【手记】小心在where中使用NEWID()的大坑

这个表达式:

ABS(CHECKSUM(NEWID())) % 3 --把GUID弄成正整数,然后取模

是随机返回0、1、2这三个数,不可能返回其它东西,但是如果把它用在where里面,就会发生很神奇的事情,比如这个查询:

【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed
--创建一个只有1列3行的表,存放0,1,2三个值
DECLARE @t TABLE(Col1 int)
INSERT @t
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2

--然后随机查一行
SELECT *
FROM @t
WHERE Col1 = ABS(CHECKSUM(NEWID())) % 3
【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

按说每次执行会且只会得到一个0~2的数字,但多跑两次,你会得到各种神奇的结果,有返回两行的,有不返回的,肥肠蹊跷,一度让我怀疑人生,直到发现我不是一个人:

https://*.com/questions/38498513/odd-sql-server-tsql-query-results-with-newid-in-the-where-clause

你可以认为是查询是这样的:
表中有三行数据,
读取第1行,生成一个全新的随机NEWID,对其计算checksum,对3取余,看第1个Id是否等于这个结果
读取第2行,生成一个全新的随机NEWID,对其计算checksum,对3取余,看第2个d是否等于这个结果
读取第3行,生成一个全新的随机NEWID,对其计算checksum,对3取余,看第3个d是否等于这个结果

结果当然可以返回大于1行,最大可能是3行都匹配到了,最小可能是0行匹配

如果你要随机去一行的话,可以SELECT top 1 *
FROM @t order by newid()

非要用NEWID去余的话,也可以这样子,定义一个(重点是一个NEWID)对其做checksum后在%3运算
DECLARE @id uniqueidentifier 
set @id = NEWID()

--然后随机查一行
SELECT *,NEWID()
FROM @t
WHERE Col1 = ABS(CHECKSUM(@id)) % 3

我想我明白奥义了,根源在于newid()的行为与别的函数不一样,在一次查询中它并不是执行1次然后把结果缓存起来再代入查询,而是每行都执行,这也是select newid()能得到不同guid的原因,所以并不是在where中才特殊,而是一直特殊。但不得不说这确实很反直觉,再次感谢您的点拨。

【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题

刚装好SSMS 17.1准备体验,弹出:

【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

一番搜索,普遍办法都是安装VS2015独立shell、删除某个注册表项什么的,没用,首先这个shell我是装了的,然后也没有那个注册表项。我自己尝试过重装shell、重装SSMS17,都没用。

然后尝试【修复】VS2015独立shell后,问题解决。具体操作:

  1. 在添加删除程序里找到【Microsoft Visual Studio 2015 Shell(独立)】并右键→更改
  2. 在稍候的安装界面中,选【修复】
  3. 在修复进度到一半的时候,实际上就已经可以打开SSMS17了

如果没有这个东西,点这里下载。

【C#】组件分享:FormDragger窗体拖拽器

适用:.net2.0+ winform项目

介绍:

类似QQ、迅雷等讲究UI体验的软件,都支持在窗口内多处地方拖动窗口,而不必老实巴交的去顶部标题栏拖,这个组件就是让winform也能这样随性拖拽,随性度或更甚。先看效果:

【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

可拖拽的地方包括不限于:

  • 窗体、Panel、GroupBox、TabControl等容器控件的空白区;
  • 菜单栏、工具栏、状态栏等bar的空白区,以及无效项目;
  • Label、PictureBox、ProgressBar等通常不与鼠标交互的控件;
  • 一切无效控件(Enabled为false);

基本上就是你觉得应该可以拖的地方都可以拖。

用法:

先看公开成员:

【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed
//拖拽器开关
bool Enabled { get; set; }

//排除列表。可向其中添加或移除控件实例,处于列表中的控件不接受拖拽
List<Control> ExcludeControls { get; }

//事件:准备拖拽时发生,可用e.Cancel = true取消拖拽,e还携带其它信息
event EventHandler<FormDraggingCancelEventArgs> Dragging;

//事件:拖拽器开关状态改变后
event EventHandler EnabledChanged;
【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

使用挺简单,随时随地FormDragger.Enabled = true/false就能开闭拖拽功能,比如在Main函数中就可以开好,完了程序内的所有自建窗体就可以愉快的拖拽了,但是,像消息框MessageBox、各种对话框(如打开文件对话框)等由系统提供的窗体不能拖,原因是这些窗口的消息不进入程序,需要勾子才能捕获到,犯不着(其实方案里已经实现了一个DialogDragger.cs,就是用来拖系统对话框的,但已知颜色选择对话框ColorDialog存在问题,所以暂时没集成,后面感觉有必要且解决了再更新,建议Watch)。关于消息框,也可以选用这个,由于是自制,所以可以拖。

对于适用拖拽规则的控件,鼠标左键点击消息(如MouseDown)是到不了它的,因为被拦截了,所以注册了这类事件也不会触发,若希望某个可拖控件不被拖到,例如某个图片框,你希望它具备“超链”的功能,点上去时执行注册好的MouseDown事件处理方法,那么有两种方式可以实现例外:

  1. 将该控件加入例外列表:FormDragger.ExcludeControls.Add(pictureBox1);
  2. 注册FormDragger.Dragging事件,在事件处理方法中,传入的e有一个Control属性,表示点到的控件,所以可以判断e.Control是否你要例外的控件,若是,令e.Cancel = true即可;此外e还携带别的信息,如鼠标位置、坐标类型等供辅助判断;

以上场景在源码中都有示例供参考。

原理:

利用Application.AddMessageFilter向程序加入消息过滤器,拦截并处理发往程序窗体的鼠标左键单击消息,若满足逻辑,则拦下该消息,并往控件所在的窗体发送点击标题栏的消息,达到点击该控件时系统认为是点到窗体标题栏的效果。更多信息请前往下面的地址查看。

方案所在:

https://github.com/ahdung/FormDraggerDemo【优先】

https://coding.net/u/ahdung/p/FormDraggerDemo/git

http://git.oschina.net/ahdung/FormDraggerDemo

方案中已写测试器,就是截图那个样子,欢迎下载体验。

【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码:

【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed
//将字符串"a"写入流,再拿到流的字节组data
using (var ms = new MemoryStream())
{
    using (var bw = new BinaryWriter(ms))
    {
        bw.Write("a");
    }
    byte[] data = ms.ToArray();
}
【手记】小心在where中使用NEWID()的大坑  【手记】解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题  【C#】组件分享:FormDragger窗体拖拽器  【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

因为字母a的utf8编码是97,所以我预期data只有1个元素且值为97,而实际上,data有两个元素,依次为1、97,显然97代表a,但前面的1是什么鬼,再试其它字符串,仍然会在前面多出1个甚至多个字节,值也比较飘忽,总之就是bw并没有老老实实地【只】写入string的二进制,而是加了些料,这在严格要求字节正确的场景会出问题,如http请求体,服务器会对这些多出来的字节表示懵逼。遂搜索一番,发现MSDN*早有提到,前面多出来的字节实际上是表示string的长度,叫长度前缀(length-prefixed),据SO某答主的说法,这是供BinaryReader的ReadString方法用,知道长度,它才知道要读取到哪里。所以如果流的读取方不是BinaryReader,这些长度前缀就是多余甚至是有害的,这种情况下就不能使用BinaryWriter.Write(string)方法,要写入干净的string二进制,可以这样:

bw.Write(Encoding.UTF8.GetBytes("a"));//按需选用正确的编码

即先用具体编码得到string的字节组,再用BinaryWriter.Write(byte[])写入该字节组,当然构造bw时指定何种编码就无所谓了。

-文毕-