贝网博客

我的分类
流水记事
源码下载
Asp.net
其它
数据库
Javascript
.Net技术
我的相册
友情链接
博客园
CSDN博客
Start0
最新回复
嗷嗷的cCC
fasdfasdf
[:..
lz这个东西好厉害,我..
哈哈,好照片
不错,以前一直用黄色..
终于找到支持ff的修正..
终于找到支持ff的修正..
新鲜性
看看,试试,好不好使。
 日志列表    
本站一共有博客83条,当前显示81条
 发表于:2011-12-23 14:19:53

原理就是用DataLenth函数得到的长度对Len函数得到的长度比较,如果不相等,则包含中文,如:
Select * from Table1 where DATALENGTH(RTRIM(LTRIM(Col1)))<> LEN(Col1)

说明:Len函数会先对字段进行Trim去空格,再取长度,而DataLength则直接取长度,所以必须对字段先进行Trim,再取长度与Len比较

注:上述方法要求字段类型是Varchar,如果字段类型是NVarchar,这篇文章所说的方法不生效

 发表于:2011-07-29 11:23:05
更新于:2011-12-12 15:35:26

本文简单介绍如何使用XmlSerializer或BinaryFormatter来把对象序列化成xml文件或二进制文件

在应用中,有些数据库查询非常耗时,而且变化又不是非常大,一开始我的处理是把数据取出来后,放入到Cache中,后来发现由于内在消耗比较大,应用程序池经常回收,导致缓存丢失,于是决定改用序列化到文件的方式,序列化到文件有2种方案,一种是二进制文件,一种是xml文件,后者可以随意编辑,适合配置文件或需要修改数据的场合,通用方法如下:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;

/// <summary>
/// 序列化辅助类
/// </summary>
public class SerializeHelper
{
    /// <summary>
    /// 序列化对象为二进制文件
    /// </summary>
    /// <typeparam name="T">需要序列化的对象类型,必须声明[Serializable]特征,且必须是public类</typeparam>
    /// <param name="obj">需要序列化的对象</param>
    /// <param name="serializeFilePath">序列化后的物理文件路径</param>
    public static void BinarySerialize<T>(T obj, string serializeFilePath)
    {
        using (var fs = new FileStream(serializeFilePath, FileMode.Create))
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(fs, obj);
        }
    }
    /// <summary>
    /// 反序列化二进制文件为对象
    /// </summary>
    /// <typeparam name="T">需要序列化的对象类型,必须声明[Serializable]特征</typeparam>
    /// <param name="serializeFilePath">反序列化对象的物理文件路径</param>
    public static T BinaryDeserialize<T>(string serializeFilePath) where T : class
    {
        using (var fs = new FileStream(serializeFilePath, FileMode.Open))
        {
            var formatter = new BinaryFormatter();
            return formatter.Deserialize(fs) as T;
        }
    }

    /// <summary>
    /// 序列化对象为XML文件
    /// </summary>
    /// <typeparam name="T">
    /// 需要序列化的对象类型,必须声明[Serializable]特征,且属性类也必须声明[Serializable]特征。
    /// 如果属性是抽象类或接口,必须声明[System.Xml.Serialization.XmlInclude(typeof(子类))]特征
    /// </typeparam>
    /// <param name="obj">需要序列化的对象</param>
    /// <param name="serializeFilePath">序列化后的物理文件路径</param>
    public static void XmlSerialize<T>(T obj, string serializeFilePath)
    {
        using (var sw = new StreamWriter(serializeFilePath, false))
        {
            var formatter = new XmlSerializer(typeof(T));
            formatter.Serialize(sw, obj);
        }
    }
    /// <summary>
    /// 反序列化XML文件为对象
    /// </summary>
    /// <typeparam name="T">
    /// 需要序列化的对象类型,必须声明[Serializable]特征,且属性类也必须声明[Serializable]特征。
    /// 如果属性是抽象类或接口,必须声明[System.Xml.Serialization.XmlInclude(typeof(子类))]特征
    /// </typeparam>
    /// <param name="serializeFilePath">反序列化对象的物理文件路径</param>
    public static T XmlDeserialize<T>(string serializeFilePath) where T : class
    {
        using (var sr = new StreamReader(serializeFilePath))
        {
            var formatter = new XmlSerializer(typeof(T));
            return formatter.Deserialize(sr) as T;
        }
    }
}
 

调用简单说明:
    void Main(){
        s q = new s();
        q.a1 = new b(){c=2222};
        q.a1.b = "q.a1.b";
        q.a3=new List<string>();
        q.a3.Add("aaa");
        q.a3.Add(DateTime.Now.ToString());
        SerializeHelper.XmlSerialize(q, "e:\\a.bc");
        var b = SerializeHelper.XmlDeserialize<s>("e:\\a.bc");
    }
    [Serializable]
    public class s
    {
        public a a1;
        public List<string> a3;
    }
    [Serializable]
    [System.Xml.Serialization.XmlInclude(typeof(b))]
    public abstract class a
    {
        public string b;
    }
    [Serializable]
    public class b : a
    {
        public int c;
    }
 

回复:1  发表于:2010-09-02 17:58:00
更新于:2011-12-12 15:34:19

以前使用Oracle,觉得它的递归查询很好用,就研究了一下SqlServer,发现它也支持在Sql里递归查询

下面介绍Sql语句里的递归查询 分SqlServer2005和Oracle 两个版本
并举了2个例子说明:
一个是在同一张表里存储id和父id的情况
另一个是计算销售金额返利(减去以前返利的数据)

举例说明:
SqlServer2005版本的Sql如下:
比如一个表,有id和pId字段,id是主键,pid表示它的上级节点,表结构和数据:
CREATE TABLE [aaa](
 [id] [int] NULL,
 [pid] [int] NULL,
 [name] [nchar](10)
)
GO
INSERT INTO aaa VALUES(1,0,'a')
INSERT INTO aaa VALUES(2,0,'b')
INSERT INTO aaa VALUES(3,1,'c')
INSERT INTO aaa VALUES(4,1,'d')
INSERT INTO aaa VALUES(5,2,'e')
INSERT INTO aaa VALUES(6,3,'f')
INSERT INTO aaa VALUES(7,3,'g')
INSERT INTO aaa VALUES(8,4,'h')
GO

--下面的Sql是查询出1结点的所有子结点
with my1 as(select * from aaa where id = 1
 union all select aaa.* from my1, aaa where my1.id = aaa.pid
)
select * from my1 --结果包含1这条记录,如果不想包含,可以在最后加上:where id <> 1

--下面的Sql是查询出8结点的所有父结点
with my1 as(select * from aaa where id = 8
 union all select aaa.* from my1, aaa where my1.pid = aaa.id
)
select * from my1;

 --下面是递归删除1结点和所有子结点的语句:
with my1 as(select * from aaa where id = 1
   union all select aaa.* from my1, aaa where my1.id = aaa.pid
)
delete from aaa where exists (select id from my1 where my1.id = aaa.id) 

Oracle版本的Sql如下:
比如一个表,有id和pId字段,id是主键,pid表示它的上级节点,表结构和数据请参考SqlServer2005的,Sql如下:
--下面的Sql是查询出1结点的所有子结点
 SELECT * FROM aaa
  START WITH id = 1
CONNECT BY pid = PRIOR id

--下面的Sql是查询出8结点的所有父结点
 SELECT * FROM aaa
  START WITH id = 8
CONNECT BY PRIOR pid = id

今天帮别人做了一个有点意思的sql,也是用递归实现,具体如下:
假设有个销售表如下:
CREATE TABLE [tb](
    [qj] [int] NULL,    -- 月份,本测试假设从1月份开始,并且数据都是连续的月份,中间没有隔断
    [je] [int] NULL,    -- 本月销售实际金额
    [rwe] [int] NULL,    -- 本月销售任务额
    [fld] [float] NULL    -- 本月金额大于任务额时的返利点,返利额为je*fld
) ON [PRIMARY]
现在要求计算每个月的返利金额,规则如下:
1月份销售金额大于任务额  返利额=金额*返利点
2月份销售金额大于任务额  返利额=(金额-1月份返利额)*返利点
3月份销售金额大于任务额  返利额=(金额-1,2月份返利额)*返利点
以后月份依次类推,销售额小于任务额时,返利为0
具体的Sql如下:
WITH my1 AS (
                SELECT *,
                       CASE
                            WHEN je > rwe THEN (je * fld)
                            ELSE 0
                       END fle,
                       CAST(0 AS FLOAT) tmp
                FROM   tb
                WHERE  qj = 1
                UNION ALL
                SELECT tb.*,
                       CASE
                            WHEN tb.je > tb.rwe THEN (tb.je - my1.fle -my1.tmp)
                                 * tb.fld
                            ELSE 0
                       END fle,
                       my1.fle + my1.tmp tmp -- 用于累加前面月份的返利
                FROM   my1,
                       tb
                WHERE  tb.qj = my1.qj + 1
            )
SELECT *
FROM   my1

 发表于:2011-12-12 14:53:51

白天正常的时候还好,很少咳嗽,但是午睡或晚上睡觉一段时间后,就开始咳嗽的很厉害,经常会把喝的奶也吐出来,去省妇幼看了好多次,边上一个老中医那里也看了很多次,依然不见效,天气一热就有好转,天一冷,好像又厉害了,上周看了老中医,吃了两付中药,吃完还拉肚子,骗她吃中药,要拿个棒棒糖,非常配合,添一个糖,吃一口中药,好可爱,可惜就是没效果。
唉,今天晚上再次去看下老中医,看看怎么样,想想小贝可爱的样子,再想想她咳嗽的哭的样子,真心疼……

 发表于:2010-09-06 15:56:42
更新于:2011-12-12 14:49:04

以前用了5,6年的Oracle,对SqlServer已经很生疏了,加个帖子记录日常应用SqlServer中的方法或技巧吧
收集了一些在SqlServer2005中的应用技巧,比如查看数据库版本、获取随机数、获取行号、触发器例子等

1、获取刚刚插入的表的自增ID值:
有3个方法
1、select @@identity :返回当前会话的所有作用域中的任何表最后生成的标识值(全局的,如果你插入表a,又插入表b,此时返回b表的id)
2、select SCOPE_IDENTITY() :返回当前会话和当前作用域中的任何表最后生成的标识值 (是当前会话的,如果你插入表a,又插入表b,此时返回b表的id)
3、select ident_current(‘表名'):返回特定表最后生成的标识值 (不受作用域和会话的限制)
在实际应用中,使用第3种方式在并发比较大的时候,可能会造成问题,例如下面的代码:
insert into table1(xxx) values(xxx); select ident_current('table1');
上面这句sql,在并发用户很多的时候,很有可能返回的并不是刚刚插入的那个id,而有可能是其它线程插入的id(因为
ident_current是跨所有会话的)

2、Update语法的其它格式
今天看到一个Update语句:UPDATE a SET a.name = 'a' FROM tb1 AS a INNER JOIN tb2 AS b ON b.id = a.id
一时非常困惑,后来查询资料才知道,这个语法类似于Oracle的:
UPDATE tb1 a SET a.name = 'a' WHERE EXISTS (SELECT 1 FROM tb2 b WHERE b.id = a.id)
还有DELETE i FROM tb1 as i, tb2 as j WHERE i.id = j.id

3、获取随机数
a.根据Guid作为种子来获取随机数:
declare @g uniqueidentifier
set @g = newid()
select rand(cast(cast(@g as varbinary) as int))
上面代码效果类似于C#里的:new Random(Guid.NewGuid().GetHashCode()).Next()
b.获取1到100之间的随机数(含1和100):
select cast(ceiling(rand() * 100) as int)
c.获取100到1000之间的随机数,在b的结果基础上操作:
declare @begin int
declare @end int
set @begin=100-1
set @end=200
select (cast(ceiling(rand() * (@end-@begin)) as int)+@begin)

4、获取类似于Oracle里的Rownum的行序号:Row_Number()
比如有学生表字段:学号 所属班级
直接按学号排序得到行号的Sql:select row_number() over(order by 学号), 学号, 所属班级 from 学生表
按所属班级分类获取行号的Sql:select row_number() over(partition by 所属班级 order by 学号), 学号, 所属班级 from 学生表

5、数据库报错:从 char 数据类型到 datetime 数据类型的转换导致 datetime 值越界
因为SqlServer支持的DateTime区间为:从 1753 年 1 月 1 日到 9999 年 12 月 31 日,而C#支持的最小值是1年1月1日,所以在用到sql里时,要判断C#的日期是否小于SqlServer的这个区间

6、把select出来的结果集另存到新表中
Select * into NewTableName from table1 where 条件;// 条件为1=2时,只复制表结构,注意:不含主键和索引等信息
在Oracle中是:
Create Table NewTableName as select * from table1 where 条件

7、Cross Apply 和 Outer Apply:
在实际应用中,经常有这种情况,A表和B表是1对多的关系,现在要根据A表从B表匹配出一条记录,比如下面的2个表
学生表:

ID Name
1 张三
2 李四
成绩表:

UserId Subject Score
 1  数学  90
 2  数学  80
 1  语文  95
 2  语文  75

现在要取出姓名和成绩最高的一门课程的SQL:
Select a.Name, c.Subject, c.Score From 学生表 a Outer Apply(Select Top 1 Subject,  Score From 成绩表 b Where b.Userid = a.Id Order By Score) c
当然上面的Sql也可以用Group By实现,但是有些情况下Group By是不好实现的,就可以用Apply实现
而Cross Apply类似于Inner Join,就是Apply后面如果没有记录出来,那么Apply前面的表记录也不显示
Outer Apply类似于Left Join,就是,Apply前面的表记录始终显示

8、触发器的示例:
CREATE TRIGGER [tr_trigName]
   ON  [table1]
   AFTER INSERT,DELETE,UPDATE
AS
BEGIN
 SET NOCOUNT ON;
 
 IF EXISTS(SELECT 1 FROM INSERTED) AND EXISTS(SELECT 1 FROM DELETED)
 BEGIN 
  -- PRINT 'UPDATE的操作'
  -- 因为SqlServer不支持行触发,所以要用CURSOR游标来循环增删改的数据,下面就是循环了
  DECLARE @ID INT, @VALUE VARCHAR(100)
  DECLARE CUR CURSOR LOCAL FORWARD_ONLY FOR SELECT id, value FROM DELETED
  OPEN CUR
  FETCH NEXT FROM CUR INTO @ID, @VALUE
  WHILE @@FETCH_STATUS = 0
  BEGIN 
   SELECT @ID, @VALUE
   FETCH NEXT FROM CUR INTO @ID, @VALUE
  END
  CLOSE CUR
  DEALLOCATE CUR
 END
 ELSE  IF EXISTS(SELECT 1 FROM INSERTED)
 BEGIN
  PRINT 'INSERT的操作'
 END
 ELSE
 BEGIN
  PRINT 'DELETE的操作'
  SELECT * FROM DELETED WHERE id > 100
 END
END

9、查看数据库版本信息:
--查看数据库版本信息 1
SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY('edition')
--查看数据库版本信息 2
SELECT @@VERSION
--查看系统信息
exec master..xp_msver
下面是Oracle的查看数据库版本的SQL:
select version FROM Product_component_version Where SUBSTR(PRODUCT,1,6)='Oracle'

10、重建索引:
--查看索引碎片信息
DBCC SHOWCONFIG('表名')
--重建索引
DBCC DBREINDEX(表名, '索引名,空为所有索引', 70)--70为填充因子,可以为空
--重建数据库所有表索引
EXEC sp_msforeachtable 'DBCC DBREINDEX(''?'')'

11、清除Sql Server Management Studio里输入的密码历史记录:
a、清除Sql2008的密码历史:删除文件:C:\Documents and Settings\登录用户名\Application Data\Microsoft\Microsoft SQL Server\100\Tools\Shell\SqlStudio.bin
b、清除Sql2005的密码历史:删除文件:C:\Documents and Settings\登录用户名\Application Data\Microsoft\Microsoft SQL Server\90\Tools\Shell\mru.dat
注意:某些设置可能丢失,请注意备份

12、快速查看表的记录总数(注:对记录总数要求不精确、且没有where条件时可以使用)
--查看单个表
select rows from sysindexes where id = object_id(tablename) and indid <= 1
--查看全部表
SELECT a.id, b.[name], a.ROWS FROM sysindexes a, sys.tables b WHERE a.id = b.[object_id] AND a.indid <=1

13、实现类似MySql里Group_Concat功能:
比如表如下:
1000010 10000
1000010 11000
1000010 11101
1000010 11102
1000010 11111
1000010 11112
下面的sql:
SELECT top 1 lf_id, STUFF((SELECT ',' + CAST(fw_id AS NVARCHAR) FROM SoftFrameworkMap b WHERE  b.lf_id = A.lf_id FOR XML PATH('')),1,1,'') aaa
FROM   SoftFrameworkMap a WHERE lf_id = 1000010
得到如下结果:
1000010 10000,11000,11101,11102,11111,11112
 

 发表于:2011-04-28 10:25:59
更新于:2011-11-18 14:08:08

1、Select后面的列指定的别名,可以在Order by中使用,而不能在where中使用,如:
Select id as newName from table where id<100 order by newName

2、在编程时交换2个变量的值,需要中间变量:int tmp = a1; a1 = a2; a2=tmp;
但是在sql中不需要,下面这个sql直接可以运行以交换a1和a2两列的值:Update table1 set a1 = a2, a2 = a1
这个sql也可以给a1加上最大的a1值,而不用担心max(a1)会随着操作变化而变化Update table1 set a1 = a1 + (Select max(a1) from table1)
也就是说,我们要把这些操作理解为瞬间同时发生

3、WITH TIES关键字:只有sql同时指定了TOP和ORDER BY关键字时才能使用:
SELECT TOP 10 * FROM tb1 ORDER BY id -- 只返回前10条记录
SELECT TOP 10 WITH TIES * FROM tb1 ORDER BY id -- 先返回前10条记录,再从剩余记录中找到id等于第10条记录的那些记录返回(最终返回结果可能不止10条)

4、Outer join被Sqlserver当成Inner join的情况
对于Outer join的sql,比如Left outer join,会把左边表未匹配到的右表字段值作为null输出,如果sql对这些字段的null进行了过滤,那么Sqlserver的执行计划,会把它优化为Inner join处理,如下面的2条sql生成的执行计划完全一致:
select * from a left join b on a.id = b.id where b.id > 10;  -- b.id>10导致b.id为null的记录都出不来,所以会被优化为inne
select * from a inner join b on a.id = b.id where b.id > 10;

5、集合操作
UNION ALL:返回前后输入的所有行,包括重复行
UNION:返回前后输入的所有行,重复行只输出一行(类似于加了DISTINCT)
EXCEPT:从前面返回的行,剔除后面的行,再输出
INTERSECT:返回前后两个输入中都出现过的不重复行

 发表于:2011-04-13 10:14:45
更新于:2011-11-18 10:34:38

在业务中,有时会有这种需求,即每种分类取2条记录,比如有图书表,在首页要求每种图书显示2本
表结构和数据如下:

id bookname categoryId
1 十万个为什么 1
2 钢铁怎么烧成的 1
3 C#入门经典 2
4 C#高级编程 2
5 C#编程参考 2
6 人生 1

 现在要在categoryId为1的找2本,categoryId为2的找2本,按id逆序,sql如下:
SELECT * FROM (SELECT 图书表.*, ROW_NUMBER() OVER(PARTITION BY categoryId ORDER BY id desc) rn FROM 图书表) b WHERE rn < 3

返回数据如下:

id bookname categoryId
2 钢铁怎么烧成的 1
4 C#高级编程 2
5 C#编程参考 2
6 人生 1

回复:4  发表于:2011-04-12 20:18:10
更新于:2011-11-18 10:34:18

在DataTable里如果有一列数字,类型是字符型时,Select里的order排序,会按字符排序,而不是数值排序
比如:111, 99,  88
排序后会是:111, 88, 99
如果希望按数值排序,可以添加一列转换为数值后的列,如字符列名为bb,则:
dt.Columns.Add("aa", typeof(int), "Convert(bb, 'System.Int32')");
然后按aa列进行排序就ok了,下面是测试代码:
DataTable dt = new DataTable();
dt.Columns.Add("bb", typeof (string));

DataRow row = dt.NewRow();
row[0] = "101";
dt.Rows.Add(row);
row = dt.NewRow();
row[0] = "99";
dt.Rows.Add(row);
row = dt.NewRow();
row[0] = "88";
dt.Rows.Add(row);

dt.Columns.Add("aa", typeof(int), "Convert(bb, 'System.Int32')");
DataRow[] rows = dt.Select("", "aa");
foreach (DataRow dataRow in rows)
    Console.WriteLine(dataRow[0]);


©2008 Beinet.cn 版权所有