前一篇博客 IP to Integer 提供的 SQL 函数是 IP 转换成 Integer 的方法。 Integer 的最大值为: 2147483647(2^31 - 1)。按照 IP 转换成整数的算法,会算出来大于这个值的,所以之前提供的 IP 转换成 Integer 的方法会算出负值。
下面是 IP to BigInt 的转换算法, 这时候运算就不会小于零。
CREATE FUNCTION dbo.ipStringToBigInt
(
@ip CHAR(15)
)
RETURNS bigint
AS
BEGIN
DECLARE @rv bigint,
@o1 bigint,
@o2 INT,
@o3 INT,
@o4 INT
SELECT
@o1 = CONVERT(INT, PARSENAME(@ip, 4)),
@o2 = CONVERT(INT, PARSENAME(@ip, 3)),
@o3 = CONVERT(INT, PARSENAME(@ip, 2)),
@o4 = CONVERT(INT, PARSENAME(@ip, 1))
IF (@o1 BETWEEN 0 AND 255)
AND (@o2 BETWEEN 0 AND 255)
AND (@o3 BETWEEN 0 AND 255)
AND (@o4 BETWEEN 0 AND 255)
BEGIN
SET @rv = (@o1 * 16777216) +
(@o2 * 65536) +
(@o3 * 256) +
(@o4)
END
ELSE
SET @rv = -1
RETURN @rv
END
go
CREATE FUNCTION dbo.ipBigIntToString
(
@ip bigint
)
RETURNS CHAR(15)
AS
BEGIN
DECLARE @o1 INT,
@o2 INT,
@o3 INT,
@o4 INT
IF @ip > 4294967295 RETURN '255.255.255.255'
IF @ip <= 0 RETURN '0.0.0.0'
SET @o1 = @ip / 16777216
SET @ip = @ip % 16777216
SET @o2 = @ip / 65536
SET @ip = @ip % 65536
SET @o3 = @ip / 256
SET @ip = @ip % 256
SET @o4 = @ip
RETURN
CONVERT(VARCHAR(4), @o1) + '.' +
CONVERT(VARCHAR(4), @o2) + '.' +
CONVERT(VARCHAR(4), @o3) + '.' +
CONVERT(VARCHAR(4), @o4)
END
go
-- 调用例子
select dbo.ipBigIntToString(3708279131)
select dbo.ipStringToBigInt('221.7.217.91')
有时候我们要判断某个IP是否在某个段内,如何实现呢? 一个简单办法就是把IP转换成Integer,然后判断整数是否在某个范围内就可以实现这个判断。
转换的算法如下:
比如我们要转换的IP为: 058.062.042.000
First Octet: 058
Second Octet: 062
Third Octet: 042
Fourth Octet: 000
计算公式如下:
(first octet * 256³) + (second octet * 256²) + (third octet * 256) + (fourth octet)
= (first octet * 16777216) + (second octet * 65536) + (third octet * 256) + (fourth octet)
= (058 * 16777216) + (062 * 65536) + (042 * 256) + (000)
= 977152512
网上有现成的服务,比如下面这个地址:
http://www.aboutmyip.com/AboutMyXApp/IP2Integer.jsp
SQL Server 的一个例子如下:
CREATE FUNCTION dbo.ipStringToInt
(
@ip CHAR(15)
)
RETURNS INT
AS
BEGIN
DECLARE @rv INT,
@o1 INT,
@o2 INT,
@o3 INT,
@o4 INT,
@base INT
SELECT
@o1 = CONVERT(INT, PARSENAME(@ip, 4)),
@o2 = CONVERT(INT, PARSENAME(@ip, 3)),
@o3 = CONVERT(INT, PARSENAME(@ip, 2)),
@o4 = CONVERT(INT, PARSENAME(@ip, 1))
IF (@o1 BETWEEN 0 AND 255)
AND (@o2 BETWEEN 0 AND 255)
AND (@o3 BETWEEN 0 AND 255)
AND (@o4 BETWEEN 0 AND 255)
BEGIN
SELECT @base = CASE
WHEN @o1 < 128 THEN
(@o1 * 16777216)
ELSE
-(256 - @o1) * 16777216
END
SET @rv = @base +
(@o2 * 65536) +
(@o3 * 256) +
(@o4)
END
ELSE
SET @rv = -1
RETURN @rv
END
go
调用范例:
select dbo.ipStringToInt('058.062.042.000')
整数转换如下:
CREATE FUNCTION dbo.ipIntToString
(
@ip INT
)
RETURNS CHAR(15)
AS
BEGIN
DECLARE @o1 INT,
@o2 INT,
@o3 INT,
@o4 INT
IF ABS(@ip) > 2147483647
RETURN '255.255.255.255'
SET @o1 = @ip / 16777216
IF @o1 = 0
SELECT @o1 = 255, @ip = @ip + 16777216
ELSE IF @o1 < 0
BEGIN
IF @ip % 16777216 = 0
SET @o1 = @o1 + 256
ELSE
BEGIN
SET @o1 = @o1 + 255
IF @o1 = 128
SET @ip = @ip + 2147483648
ELSE
SET @ip = @ip + (16777216 * (256 - @o1))
END
END
ELSE
BEGIN
SET @ip = @ip - (16777216 * @o1)
END
SET @ip = @ip % 16777216
SET @o2 = @ip / 65536
SET @ip = @ip % 65536
SET @o3 = @ip / 256
SET @ip = @ip % 256
SET @o4 = @ip
RETURN
CONVERT(VARCHAR(4), @o1) + '.' +
CONVERT(VARCHAR(4), @o2) + '.' +
CONVERT(VARCHAR(4), @o3) + '.' +
CONVERT(VARCHAR(4), @o4)
END
go
调用范例:
select dbo.ipIntToString(977152512)
C# 的例子如下:
using System;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
static long ToInt(string addr)
{
return BitConverter.ToInt32(IPAddress.Parse(addr).GetAddressBytes(), 0);
}
static string ToAddr(long address)
{
return IPAddress.Parse(address.ToString()).ToString();
// This also works:
// return new IPAddress((uint) IPAddress.HostToNetworkOrder(
// (int) address)).ToString();
}
static void Main(string[] args)
{
Console.WriteLine(ToInt("64.233.187.99"));
Console.WriteLine(ToAddr(1089059683));
Console.ReadLine();
}
}
}
参考资料:
How should I store an IP address in SQL Server?
http://sqlserver2000.databases.aspfaq.com/how-should-i-store-an-ip-address-in-sql-server.html
How to convert an IPv4 address into a integer in C#?
http://stackoverflow.com/questions/461742/how-to-convert-an-ipv4-address-into-a-integer-in-c
今天在处理一个用户名数据库时,发现有些不正常的数据存在,按照逻辑,用户名只能是数字,字母,下划线和纯中文这样的字符组合存在,不应该有其他组合存在,但是发现数据库中由于各种历史原因,有些不正常的存在,如何找到这些异常数据,在CSDN的 SQL Server 版问了这样两个问题,如下:
http://topic.csdn.net/u/20100111/14/529a21a1-3ea8-4263-a0d9-34e83be79b1d.html
http://topic.csdn.net/u/20100111/15/c2a124c5-bc5b-4626-86ce-c5b862cf5cff.html
感谢CSDN的网友帮忙解决了这个问题,下面就是解决方法的汇总:
if object_id('[t1]') is not null
drop table [t1]
create table [t1]([c] nvarchar(20))
insert [t1]
select 'aaa' union all -- 此数据不应该被搜索到
select 'bcds' union all -- 此数据不应该被搜索到
select 'a1' union all -- 此数据不应该被搜索到
select '啊' union all -- 此数据不应该被搜索到
select '^%' union all -- 应该搜索到
select 'ew1' union all -- 此数据不应该被搜索到
select '344' union all -- 此数据不应该被搜索到
select '__' union all -- 此数据不应该被搜索到
select '213_21' union all -- 此数据不应该被搜索到
select 'a_2' union all -- 此数据不应该被搜索到
select 'd' union all -- 此数据不应该被搜索到
select 'ddd' union all -- 此数据不应该被搜索到
select '电风扇' union all -- 此数据不应该被搜索到
select '★思寒★' union all -- 应该搜索到
select 'Ω' union all -- 应该搜索到
select 'トントン' union all -- 应该搜索到
select '***' union all -- 应该搜索到
select '///////' union all -- 应该搜索到
select '@-@' union all -- 应该搜索到
select '@小慧' union all -- 应该搜索到
select '~*晓菊*~' union all -- 应该搜索到
select '啊★洛' union all -- 应该搜索到
select '不思議の夜' union all -- 应该搜索到
select '(嘉宾)胡飞' union all -- 应该搜索到
select '--------------' -- 应该搜索到
select * from [t1] WHERE PATINDEX('%[0-9a-z_]%',c)=0
and PATINDEX('%[^吖-座]%',c) <> 0
另外感慨一下,CSDN的 SQL Server 版 问出问题到解决问题,竟然比Google 搜索答案还要快, SQL Server 版 的牛人真多, 而且回答的这么快,实在让人吃惊。
SQL Server 的 ltrim 和 rtrim 函数只会取消 char(32) 的字符, char(9) 之类的字符则不会剔除。今天在处理论坛的一个bug时,发现数据多了空格,就是char(9)在作怪。
一个演示SQL代码:
declare @s nvarchar(50)
select @s = char(32)+char(9)+'*'
print('%'+@s+'%')
select @s = ltrim(rtrim(@s))
print('%'+@s+'%')
print (str(len(@s)))
而 C# 中则没有类似问题,它剔除了很多非 char(32) 的空格。
演示代码:
static void Main(string[] args)
{
string s = string.Format("{0}{1}*", (char)32, (char)9);
Console.WriteLine("%" + s + "%");
Console.WriteLine(s.Length);
s = s.Trim();
Console.WriteLine("%" + s + "%");
Console.WriteLine(s.Length);
Console.ReadLine();
}
MSDN上一些空格的资料:
下表列出了被 Trim 方法移除的空白字符。第一列列出了字符的 Unicode 名称,第二列列出了标识该字符的 Unicode 码位的十六进制表示法。
(请注意,尽管传递特定字符时静态 Char.IsWhiteSpace(Char) 方法返回了 true,但该字符不一定被 Trim 方法移除。(作者注:MSDN上这句话很让我困惑,下面2个表中,Char.IsWhiteSpace中的空格都出现在Trim 中的空格了呀?))
| Unicode 名称 |
Unicode 码位 |
| CHARACTER TABULATION |
U+0009 |
| LINE FEED |
U+000A |
| LINE TABULATION |
U+000B |
| FORM FEED |
U+000C |
| CARRIAGE RETURN |
U+000D |
| SPACE |
U+0020 |
| NEXT LINE |
U+0085 |
| NO-BREAK SPACE |
U+00A0 |
| OGHAM SPACE MARK |
U+1680 |
| EN QUAD |
U+2000 |
| EM QUAD |
U+2001 |
| EN SPACE |
U+2002 |
| EM SPACE |
U+2003 |
| THREE-PER-EM SPACE |
U+2004 |
| FOUR-PER-EM SPACE |
U+2005 |
| SIX-PER-EM SPACE |
U+2006 |
| FIGURE SPACE |
U+2007 |
| PUNCTUATION SPACE |
U+2008 |
| THIN SPACE |
U+2009 |
| HAIR SPACE |
U+200A |
| ZERO WIDTH SPACE |
U+200B |
| LINE SEPARATOR |
U+2028 |
| PARAGRAPH SEPARATOR |
U+2029 |
| IDEOGRAPHIC SPACE |
U+3000 |
| ZERO WIDTH NO-BREAK SPACE |
U+FEFF |
会被 Char.IsWhiteSpace( 认为是空白字符的包括以下Unicode 字符:
- SpaceSeparator 类别的成员,该类别包括 SPACE 字符 (U+0020)。
- LineSeparator 类别的成员,该类别只包括 LINE SEPARATOR 字符 (U+2028)。
- ParagraphSeparator 类别的成员,该类别只包括 PARAGRAPH SEPARATOR 字符 (U+2029)。
- 字符 CHARACTER TABULATION (U+0009)、LINE FEED (U+000A)、LINE TABULATION (U+000B)、FORM FEED (U+000C)、CARRIAGE RETURN (U+000D)、NEXT LINE (U+0085) 和 NO-BREAK SPACE (U+0000A0)。
System.Data.SqlClient 命名空间下有个类: SqlConnectionStringBuilder ,可以帮助我们维护链接字符串。 这个类对我们下面的需求场景很有帮助。
对于同一个数据库,我们可能有正常和比较耗时的统计工作两种需求,为何互相不干扰,我们一般可以配置2个数据库链接字符串,类似如下的配置:
Persist Security Info=False;Integrated Security=true;Initial Catalog=Northwind;server=(local);
Persist Security Info=False;Integrated Security=true;Initial Catalog=Northwind;server=(local);Connect Timeout=0;
注: 在没有设置 Connect Timeout 时, Connect Timeout 默认为15秒,如果 Connect Timeout 设置为0,则表示永远不超时。这个设置是以秒为单位的。
其实我们完全可以只配置一个数据库链接字符串,然后使用 SqlConnectionStringBuilder 类来帮助我们实现不同 Connect Timeout 的需求。
演示代码如下:
// connString 原汁原味的配置的链接字符串
SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder(connString);
// 不论之前是否设置了Connect Timeout,这里都强制设置为用不超时
connStr.ConnectTimeout = 0;
// 使用新组合后的链接字符串
SqlConnection conn = new SqlConnection(connStr.ConnectionString);
参考资料:
SqlConnectionStringBuilder 类
http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqlconnectionstringbuilder(VS.80).aspx
不透明度属性(Opacity)
Opacity属性用于指定刷子的不透明度。
Opacity 属性的值以 0.0 和 1.0 之间的值表示。默认情况下元素的 Opacity 属性为 1.0。
如果 Opacity 值为 0,则表示画笔完全透明;如果值为 1,则表示画笔完全不透明。
如果值为 0.5,则表示画笔的不透明度为 50%;如果值为 0.725,则表示画笔的不透明度为 72.5%,
依此类推。小于 0 的值将被视为 0,而大于 1 的值将被视为 1。
如下图的代码所示:
<Canvas>
<Rectangle Height="100" Width="150" Canvas.Left="30" Canvas.Top="30"
Stroke="Black" StrokeThickness="1" Fill="SkyBlue">
</Rectangle>
<Rectangle Height="100" Width="150" Canvas.Left="100" Canvas.Top="50"
Stroke="Black" StrokeThickness="1" Fill="SkyBlue" Opacity="0.5">
</Rectangle>
</Canvas>
演示效果为:
左上角矩形不透明度为默认的1,右下角矩形的不透明度为50%,所以重叠部分可以看到左上角的矩形。
不透明蒙板(OpacityMask)
有时候,我们对透明度的要求更复杂,并不是希望整个图像都是一个固定的不透明度,这时候就要用到半透明蒙板了。
如下图所示: 我们对一副荷花图片使用不透明蒙板,就可以让荷花图片出现部分区域显示,部分区域不显示的效果。
一段演示Xaml代码如下:
<Canvas>
<Rectangle Height="100" Width="150" Canvas.Left="30" Canvas.Top="30"
Stroke="Azure" StrokeThickness="1" Fill="SeaGreen">
<Rectangle.OpacityMask>
<LinearGradientBrush>
<GradientStop Offset="0.56" Color="#00000000" />
<GradientStop Offset="0.25" Color="#FF000000" />
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
</Canvas>
演示效果如下图:
这里当然也可以是可以使用 前一篇中提到的 径向渐变刷子(RadialGradientBrush)。如下面例子:
<Canvas>
<Rectangle Height="100" Width="150" Canvas.Left="30" Canvas.Top="30"
Stroke="Azure" StrokeThickness="1" Fill="SeaGreen">
<Rectangle.OpacityMask>
<RadialGradientBrush GradientOrigin="0.7,0.5" Center="0.5,0.5"
RadiusX="0.5" RadiusY="0.4">
<GradientStop Color="#00000000" Offset="0" />
<GradientStop Color="#FF000000" Offset="1" />
</RadialGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
</Canvas>
执行效果如下图:
修剪图像(Clip)
有时候我们希望把一个图像作部分修剪,比如下面一副荷花的照片,我们希望修剪成椭圆形。
(没有经过剪切的图像)
(带有椭圆型的剪辑区域的图像)
它的剪切代码如下:
<Image
Source="sampleImages\Waterlilies.jpg"
Width="200" Height="150" HorizontalAlignment="Left">
<Image.Clip>
<EllipseGeometry
RadiusX="100"
RadiusY="75"
Center="100,75"/>
</Image.Clip>
</Image>
当然除了 EllipseGeometry(椭圆剪切类), 我们还可以使用其它剪切类,比如下面的 RectangleGeometry
<Canvas>
<Ellipse Height="200" Width="200" Canvas.Left="30" Canvas.Top="30" Stroke="Black" StrokeThickness="10" Fill="SlateBlue">
<Ellipse.Clip>
<RectangleGeometry Rect="0,0,100,100"/>
</Ellipse.Clip>
</Ellipse>
</Canvas>
上述代码执行效果:
注意,剪切是取得两个的重叠部分。
上面代码我们可以看到是通过设置Geometry的子类来做实现剪切的, Geometry 的子类如下:
System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Freezable
System.Windows.Media.Animation.Animatable
System.Windows.Media.Geometry
System.Windows.Media.CombinedGeometry
System.Windows.Media.EllipseGeometry
System.Windows.Media.GeometryGroup
System.Windows.Media.LineGeometry
System.Windows.Media.PathGeometry
System.Windows.Media.RectangleGeometry
System.Windows.Media.StreamGeometry
图像变形
所有的UI元素都可以变形,包括以下几种变形:
- 旋转变形(RotateTransform): 根据特定的角度进行物体旋转;
- 倾斜变形(SkewTransform):根据指定的x-y刻度进行倾斜;
- 刻度变形(ScaleTransform):根据横向和纵向进行放大和缩小;
- 翻转变形(TranslateTransform): 水平或横向移动物体;
旋转变形(RotateTransform)
在 二维 x-y 坐标系内围绕指定点按照顺时针方向旋转对象。
演示代码:
<Canvas>
<Rectangle Height="100" Width="100" Canvas.Left="70" Canvas.Top="10" Fill="Black">
<Rectangle.RenderTransform>
<RotateTransform Angle="27" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
演示效果:
倾斜变形(SkewTransform)
二维扭曲
演示代码
<Canvas>
<Rectangle Height="100" Width="100" Canvas.Left="70" Canvas.Top="10"
Fill="Red">
<Rectangle.RenderTransform>
<SkewTransform AngleX="15" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
演示效果
刻度变形(ScaleTransform)
在 二维 x-y 坐标系内缩放对象。
演示代码
<Canvas>
<Rectangle Height="100" Width="100" Canvas.Left="70" Canvas.Top="10"
Fill="Red">
<Rectangle.RenderTransform>
<ScaleTransform ScaleX="1.3" ScaleY=".5" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
演示效果
翻转变形(TranslateTransform)
在 二维 x-y 坐标系中平移(移动)对象。
演示代码
<Canvas>
<Rectangle Height="50" Width="50"
Fill="#CCCCCCFF" Stroke="Blue" StrokeThickness="2"
Canvas.Left="100" Canvas.Top="100">
<Rectangle.RenderTransform>
<TranslateTransform X="50" Y="50" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>由于是位移,演示效果截图会不明显,就不提供演示效果图了。
组合应用
我们当然可以把上面的变形组合应用,比如下面的例子
演示代码
<Canvas>
<Rectangle Height="100" Width="100" Canvas.Left="70" Canvas.Top="10"
Fill="Green">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45" />
<ScaleTransform ScaleX=".5" ScaleY="1.2" />
<SkewTransform AngleX="30"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
演示效果

昨天花了2个小时调试有时候启动了定时轮询,有时候没有启用定时轮询的bug,最后发现竟然是作用域的问题,比较汗颜,下面就是有bug的这个代码的简单写法:
using System;
using System.Timers;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Begin();
Console.ReadLine();
}
public static void Begin()
{
TimerCallback callbackMethod = new TimerCallback(Program.Check);
int pollCycleInMilliseconds = 180000;
System.Threading.Timer pollTimer = new System.Threading.Timer(
callbackMethod, null, pollCycleInMilliseconds, pollCycleInMilliseconds);
}
public static void Check(object notUsed)
{
Console.Write(DateTime.Now);
}
}
出bug是因为这里的 System.Threading.Timer pollTimer 是一个局部变量,这个变量出了他的作用域,可能会被销毁以及垃圾回收,这时候,自然就没有了定时轮询的触发了。
实际的代码比这里要复杂很多,是个WEB应用。一直没想到是作用域的问题才导致花了这么长时间找bug。把这个我碰到的bug记录下来,让碰到类似问题的人也好有思路提醒。
设置具体数据库启动Service Broker服务,如下图:
我这里试例数据库的名字为“ghj_Demo”,修改 Broker Enabled 属性为 true。
你也可以用SQL 语句来修改,修改的SQL语句如下:
ALTER DATABASE ghj_Demo SET ENABLE_BROKER
确保你将使用的数据库帐户具有必需的权限
你在后面连接这个数据库的帐户,要确保对这个数据库具有 SUBSCRIBE、 QUERY 、NOTIFICATIONS 的权限。
下面就是一个简单的代码例子,来演示查询通知。这里用到一个我自己建立的表:
这个表结构如下:
CREATE TABLE [dbo].[User](
[UserName] [nvarchar](20) NOT NULL,
[Email] [nvarchar](50) NULL
) ON [PRIMARY]
GO
演示的控制台代码如下:
using System;
using System.Data;
using System.Data.SqlClient;
namespace Demo
{
class Program
{
public static string connectionstring = "Data Source=.;Initial Catalog=ghj_Demo;Integrated Security=True";
public void DoDependency()
{
using (SqlConnection conn = new SqlConnection(connectionstring))
{
// sql is a local procedure that returns
// a paramaterized SQL string. You might want
// to use a stored procedure in your application.
string sql = "select [UserName] ,[Email] from dbo.[User]";
SqlCommand cmd = new SqlCommand(sql, conn);
// Ensure the command object does not have a notification object.
cmd.Notification = null;
//Notification specific code
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler(TestEvent);
conn.Open();
SqlDataReader r = cmd.ExecuteReader();
//Read the data here and close the reader
r.Close();
conn.Close();
Console.WriteLine("DataReader Read...");
}
}
void TestEvent(Object o, SqlNotificationEventArgs args)
{
// 注意:
// 如果Server端在很短的时间内发生了大量的改动(比如用了一个循环Update了好多行),
// OnChanged必须能迅速处理事件,否则它只会被触发一次。这个不是缺陷,
// 因为一般OnChanged事件处理函数内都要执行类似刷新缓存的操作,它只触发一次,
// 不会影响程序逻辑,却能提高程序性能。
Console.WriteLine("======================");
Console.WriteLine("Event Recd");
Console.WriteLine("Info:" + args.Info);
Console.WriteLine("Source:" + args.Source);
Console.WriteLine("Type:" + args.Type);
}
static void Main(string[] args)
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm = new SqlClientPermission(
System.Security.Permissions.PermissionState.Unrestricted);
perm.Demand();
}
catch
{
throw new ApplicationException("No permission");
}
try
{
SqlDependency.Stop(connectionstring);
//Start the listener infrastructure on the client
SqlDependency.Start(connectionstring);
Program q = new Program();
q.DoDependency();
Console.WriteLine("Wait for Notification Event...");
Console.Read();
}
finally
{
//Optional step to clean up dependency else it will fallback to automatic cleanup
SqlDependency.Stop(connectionstring);
}
}
}
}
一些注意事项:摘自MSDN文档
使用查询通知功能的应用程序需要考虑下列特殊注意事项。
|
注意事项
|
说明
|
|
SQL Server 的服务帐户
|
对于使用本地系统帐户作为服务帐户的 SQL Server 实例,应用程序不会从其接收通知。
|
|
接收通知
|
无法在运行 Windows 95 或 Windows 98 的计算机上接收通知。
|
|
查询通知和事务
|
如果在某一事务内进行了多项影响具有已注册通知请求的一组数据的修改,则仅会发送单个通知事件。
|
|
快速更新和查询通知
|
使用查询通知的应用程序必须考虑到立即出现通知的情况。服务器上的数据更改时,通知消息将发送到相应的服务中介程序队列。 应用程序需要注册才能接收其他通知。 因此,如果多个应用程序快速更新某个数据集,应用程序在缓存刷新后,立即可以接收通知,检索数据,然后获取另一个更新通知。 编写使用查询通知的应用程序时必须考虑到此情况。 如果应用程序使用不断更新的数据,则可能更适合使用另一种数据缓存策略。
|
|
设置选项设置
|
在通知请求下执行 SELECT 语句时,提交请求的连接必须设置以下选项:
· ANSI_NULLS ON
· ANSI_PADDING ON
· ANSI_WARNINGS ON
· CONCAT_NULL_YIELDS_NULL ON
· QUOTED_IDENTIFIER ON
· NUMERIC_ROUNDABORT OFF
· ARITHABORT ON
|
编写通知查询语句的约束
您可以为 SELECT 和 EXECUTE 语句设置通知。 使用 EXECUTE 语句时,SQL Server 会为执行的命令而不是 EXECUTE 语句本身注册通知。 该命令必须满足 SELECT 语句的要求和限制。 当注册通知的命令包含多个语句时,数据库引擎会为批处理中的每个语句创建一个通知。
对满足以下要求的 SELECT 语句支持查询通知:
-
SELECT 语句中的提取的列必须显式声明,且表名称必须用由两部分组成的名称进行限定。 请注意,这意味着语句中引用的所有表都必须位于同一个数据库中。
-
语句不能使用星号 (*) 或 table_name.* 语法来指定列。
-
语句不能使用未命名的列或重复的列名称。
-
语句必须引用一个基表。
-
SELECT 语句中的提取的列不能包含聚合表达式,除非该语句使用 GROUP BY 表达式。 在提供 GROUP BY 表达式的情况下,选择列表可以包含聚合函数 COUNT_BIG() 或 SUM()。 不过,不能为可以为 null 的列指定 SUM()。 语句不能指定 HAVING、CUBE 或 ROLLUP。
-
SELECT 语句中的提取的列用作简单表达式时不能出现多次。
-
语句不能包含 PIVOT 或 UNPIVOT 运算符。
-
语句不能包含 INTERSECT 或 EXCEPT 运算符。
-
语句不能引用视图。
-
语句不能包含以下任一项: DISTINCT、COMPUTE、COMPUTE BY 或 INTO。
-
语句不能引用服务器全局变量 (@@variable_name)。
-
语句不能引用派生表、临时表或表变量。
-
语句不能引用其他数据库或服务器中的表或视图。
-
语句不能包含子查询、外部联接或自联接。
-
语句不能引用大型对象类型: text、ntext 和 image。
-
语句不能使用 CONTAINS 或 FREETEXT 全文谓词。
-
语句不能使用行集合函数,包括 OPENQUERY 和 OPENROWSET。
-
语句不能使用以下任一集合函数: AVG、COUNT(*)、MAX、MIN、STDEV、STDEVP、VAR 或 VARP。
-
语句不能使用任何不确定性函数,包括排名和开窗函数。
-
语句不能包含用户定义的聚合。
-
语句不能引用系统表或视图,包括目录视图和动态管理视图。
-
语句不能包含 FOR BROWSE 信息。
-
语句不能引用队列。
-
语句不能包含无法更改或无法返回结果的条件语句(例如 WHERE 1=0)。
sunmast 对查询通知的注意事情也有很多有价值的整理:
使用SQL Server 2005 Query Notification的几个注意事项
http://blog.joycode.com/sunmast/archive/2006/08/11/sql_2005_query_notification_comments_79814.aspx
参考资料:
剖析SQL Server 2005查询通知之基础
http://www.allwiki.com/wiki/%E5%89%96%E6%9E%90SQL_Server_2005%E6%9F%A5%E8%AF%A2%E9%80%9A%E7%9F%A5%E4%B9%8B%E5%9F%BA%E7%A1%80
Using SqlDependency for data change events
http://www.codeproject.com/KB/database/chatter.aspx
SQL Server 2005 Service Broker 初探
http://msdn.microsoft.com/zh-cn/library/ms345108.aspx
SQL Server 2005数据库开发详解
http://book.csdn.net/bookfiles/24/10024713.shtml
C# Windows 应用程序中实现 SQL Server 2005 查询通知
http://support.microsoft.com/kb/555893/zh-cn
SqlDependency changes for RTM [Sushil Chordia]
http://blogs.msdn.com/dataaccess/archive/2005/09/27/474447.aspx
.NET 2.0 SqlDependency快速上手指南
http://www.cnblogs.com/Xrinehart/archive/2006/07/27/461106.html
在 Windows 应用程序中使用 SqlDependency
http://msdn.microsoft.com/zh-cn/library/a52dhwx7(VS.80).aspx
Using SqlDependency in an ASP.NET Application
http://msdn.microsoft.com/en-us/library/9dz445ks(VS.80).aspx
Minimum Database Permissions Required for SqlDependency
http://www.codeproject.com/KB/database/SqlDependencyPermissions.aspx
使用SQL Server 2005 Query Notification的几个注意事项
http://blog.joycode.com/sunmast/archive/2006/08/11/sql_2005_query_notification_comments_79814.aspx
在建立存储过程前,我习惯于先检查存储过程是否存在,如果存在就建立,然后再创建。
这个检查的过程,现在有2种习惯写法,如下:
if exists (
select * from information_schema.routines where specific_name = 'WorkOrdersForBlade' and specific_schema = 'dbo')
begin
drop procedure dbo.workordersforblade
end
go
或者
if exists (
select * from sysobjects where type = 'p' and name = 'WorkOrdersForBlade')
begin
drop procedure dbo.workordersforblade
end
go
information_schema.routines 是SQL Server 2000开始新加的系统视图,它是以 sysobjects 和 syscolumns 系统表为基础建立的系统视图。它的字段更具备可读性。
用上面那个写法都没有问题。 在SQL Server 2005 以及 2008 的默认模板中,使用的是第一种写法。
显然,我们最好用经过整合后,更具备可读性的视图 information_schema.routines 。
参考资料:
SQL Server 2008 联机丛书 对routines 视图的介绍
http://msdn.microsoft.com/zh-cn/library/ms188757.aspx
http://msdn.microsoft.com/en-us/library/ms188757.aspx
数据库中User和Schema的关系
http://blog.csdn.net/yanjiangbo/archive/2007/09/12/1782576.aspx
ROUTINES
http://www.yesky.com/imagesnew/software/tsql/ts_ia-iz_3kq1.htm
公司正在统一存储过程的编写规范,为了更宜用,我们就修改了SQL Server Management Studio 和 Microsoft Visual Studio 2008 的模板文件。
SQL Server Management Studio 的模板文件所在的目录请参看我之前的博客:
改变 SQL Server Management Studio的模板
Visual Studio 2008 的DataBase Project(如下这个数据库项目)的模板则在
C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\Templates\Database Project Items 这个目录下。
注意:这跟其他类型项目的模板不是一个目录。
我这里要修改的模板就是下面方式建立的存储过程的模板。

额外多说一句,我是通过下面这个工具找到这个目录的。
Process Monitor
v2.03 (December 10, 2008)
Monitor file system, Registry, process, thread and DLL activity in real-time.
参考资料:
vs2008修改模板-自定义版权信息