NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计..._anmei1912的博客-程序员秘密

技术标签: c#  测试  数据库  

NET MVC全局异常处理(一)

 

.NET MVC全局异常处理

一直知道有.NET有相关的配置,但没有实际做过,以为改下设定就可以,结果实际使用的时候还是遇到不少问题,所以要记录一下。

IIS配置

刚开始不想改程序代码,所以直接就想到了IIS里面的错误页配置配置,一开始反复测试,设置改了很多,但是没有效果,后来发现是静态页的配置,还没有进入MVC的程序部分,所以对于.NET MVC这种动态页是不生效的,应该使用.NET错误页选项

静态错误页配置

静态页配置流程如下:



如上图所示,IIS中配置对错误的响应有三种方式,默认情况是第三个,本地访问显示详细错误信息,外部地址访问显示自定义页面,这样方便开发者调试,如果没有设置专门的错误页会使用IIS自带的样式,也就是第二张图中的配置,根据路径我们可以找到这样一个文件夹,里面都是错误提示的静态页,对应不同的状态代码

我们可以把IIS设置为均使用自定义错误页看下效果,或者直接通过文件访问


上面那张是详细的静态404错误,可以看到会暴露我们系统路径,下面则是默认的自定义错误页

静态错误的默认页有相应的设置,看似可以修改,有“文件”、“执行URL”、“重定向”三种,但是实际设置一下就会发现报错:锁定错误

通过这个错误我们去搜索解决方法可以看到一些人说将web.config中的httperror节下的defaultPath解锁即可,但似乎这是IIS7以前的设置,在IIS10中并没有相应的选项,看到一些说明提到可能是官方使用了更加安全的管理机制,因为发现这边的配置是静态页相关,不符合我的需要,没有深入研究,如果一定要使用这种可以看看这篇博客,试试能否通过系统命令解决锁定的问题

win7 IIS Web.config节点锁定问题

.NET错误页配置

.NET错误页的设置与静态页差不多,除了入口不一样,配置的选项也不太相同,但是整体意思一样


可以看到这里要求是绝对URL,所以实际使用起来应该是不太方便,所以没有找到太多相关资料。另外,需要web.conig中的customError设为On,部分异常如500会自动跳转到MVC的默认错误页Home/Error

使用IIS的错误页处理虽然不用改代码,但是维护起来局限性很多,最终还是应该通过程序进行全局异常捕获

程序设置

通过程序控制的方法我想到两种,一个是使用全局配置文件Global.asax中的Application_Error方法,另一个是使用MVC的过滤器,默认的四种过滤器中就包含异常过滤

全局异常配置

这种方法对于WebForm和MVC都是通用的,在ASP.NET中,只要网站程序抛出未捕获的异常都会触发Application_Error事件。

使用此方法一定要把GlobalFilter全局过滤器中的HandleErrorAttribute注册取消掉,也可以将配置文件中的customErrors节点关闭,否则HTTP 500的错误将不会被Application_Error事件捕获。


捕获到异常之后我们可以很容易地跳转到静态页面

protected void Application_Error(object sender, EventArgs e) { Exception exception = Server.GetLastError(); var httpStatusCode = (exception as HttpException)?.GetHttpCode() ?? 700; //如果为空则走自定义 var httpContext = ((MvcApplication)sender).Context; httpContext.ClearError(); switch (httpStatusCode) { case 404: httpContext.Response.Redirect("~/Error/404.htm"); break; default: httpContext.Response.Redirect("~/Error/500.htm"); break; } }

在一般情况下我们也可以指向一个控制器

protected void Application_Error(object sender, EventArgs e) { Exception exception = Server.GetLastError(); var httpStatusCode = (exception as HttpException)?.GetHttpCode() ?? 700; //如果为空则走自定义 var httpContext = ((MvcApplication)sender).Context; httpContext.ClearError(); var routeDic = new RouteValueDictionary { {
                    "controller", "Home"}, { "action","Error"} }; httpContext.Response.RedirectToRoute("Default", routeDic); }

但是在实际的业务中遇到了一些http请求的问题,在处理一部分代码抛出的异常时会出现“服务器无法在已发送HTTP标头之后······”这一系列异常,如“设置状态”、“追加标头”等,这个时候跳转要使用另一种写法

protected void Application_Error(object sender, EventArgs e) { Server.ClearError(); Response.TrySkipIisCustomErrors = true; var routeData = new RouteData(); IController controller = new HomeController(); routeData.Values.Add("controller", "Home"); routeData.Values.Add("action", "Error"); controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData)); Response.End(); }

这里要注意的一点是如果要使用Area中的控制器不能写成routeData.Values.Add,而是使用DataTokens

routeData.DataTokens.Add("area", "TestArea");



【转载】网站遭遇DDoS攻击怎么办

 

在网站运维过程中,有些人的网站遭遇过DDoS攻击,DDos攻击又叫做分布式拒绝服务攻击。DDos攻击将多个计算机联合起来作为攻击平台,对一个或多个目标发动DDoS攻击,从而成倍地提高拒绝服务攻击的威力。该攻击方式利用目标系统网络服务功能缺陷或者直接消耗其系统资源,使得该目标系统无法提供正常的服务。该攻击导致的结果就是你的网站无法响应正常的请求,正常的流量用户无法打开的你的网站,无法访问到你的网站内容。如果你的服务器是阿里云、腾讯云这类云服务器厂商的服务器,可选购DDoS高防IP产品来阻止这类攻击行为。

先介绍下DDoS攻击防御方法:

(1)过滤不必要的服务和端口:可以使用Inexpress、Express、Forwarding等工具来过滤不必要的服务和端口,即在路由器上过滤假IP。

(2)异常流量的清洗过滤:通过DDOS硬件防火墙对异常流量的清洗过滤,通过数据包的规则过滤、数据流指纹检测过滤、及数据包内容定制过滤等顶尖技术能准确判断外来访问流量是否正常,进一步将异常流量禁止过滤。

(3)分布式集群防御:这是目前网络安全界防御大规模DDOS攻击的最有效办法。分布式集群防御的特点是在每个节点服务器配置多个IP地址(负载均衡),并且每个节点能承受不低于10G的DDOS攻击,如一个节点受攻击无法提供服务,系统将会根据优先级设置自动切换另一个节点。

(4)高防智能DNS解析:高智能DNS解析系统与DDOS防御系统的完美结合,为企业提供对抗新兴安全威胁的超级检测功能。它颠覆了传统一个域名对应一个镜像的做法,智能根据用户的上网路线将DNS解析请求解析到用户所属网络的服务器。

 

除了上述提到的几个处理办法外,如果你的服务器是云服务器厂商的服务器,则云服务器厂商一般都会提供DDoS高防IP这类的安全产品,可通过购买这类安全类的产品来快速设置阻止服务器遭遇到的DDos攻击,以下只以阿里云的DDoS高防IP产品为例。

阿里云的DDos高防IP产品是针对互联网服务器(包括非阿里云主机)在遭受大流量DDoS攻击后导致服务不可用的情况下,推出的付费服务,用户可通过配置高防IP,将攻击流量引流到高防IP,确保源站的稳定可靠。

直接上官方的帮助文档链接,不再详细阐述,相关运维人员可参考官方帮助文档:

(1)阿里云DDoS高防IP产品首页

(2)阿里云DDos高防IP产品产品介绍帮助文档

(3)阿里云DDos高防IP产品启用高防实例帮助文档

如果使用的服务器是腾讯云服务器,可参考腾讯云的官方文档:腾讯云DDoS 防护(大禹)

 





使用 HttpRequester 更方便的发起 HTTP 请求

 

使用 HttpRequester 更方便的发起 HTTP 请求

Intro#

一直感觉 .net 里面(这里主要说的是 .net framework 下)发送 HTTP 请求的方式用着不是特别好用,而且在 .net framework 里发送 HTTP 请求的方式有好几种,如:WebClient/WebRequest/HttpClient,于是自己封装了一个 HttpRequester

WebClient 主要是用来下载,不能对 HTTP 做较多的自定义,HttpClient 是微软后来加入的,也是比较推荐使用的处理 HTTP 请求的,但是在 .net framework 下如果不注意的话可能会造成很大的灾难,从 .net core 2.1 开始,微软引入了 HttpClientFactory 去解决了一些问题,如果你是在 .net core 程序下跑的话,推荐使用 HttpClient,如果在 .net framework 下跑的话可以使用 WebRequest,这里说明一下,.net core 下,WebRequest 内部也是基于 HttpClient 的,详细可以参考 https://github.com/dotnet/corefx/blob/master/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs#L1096

HttpWebRequest

HttpRequester 是基于 WebRequest 封装的,使用比较简洁的 Fluent API 的方式调用,如果是在 .net framework 下开发,可以尝试使用一下,具体使用可以参考下面的示例以及 Github 上的示例代码 示例代码2

添加 Nuget 包引用#

添加对 WeihanLi.Common 的引用,需要 1.0.14 及以上版本

使用 HttpRequester#

Copy

var result = new HttpRequester("https://weihanli.xyz") // 使用 GET 方式请求 https://weihanli.xyz .Execute(); // 返回 responseText System.Console.WriteLine(result); // 使用 POST 方法请求 https://accounting.weihanli.xyz/Account/LogOn var loginResult = new HttpRequester("https://accounting.weihanli.xyz/Account/LogOn", HttpMethod.Post) .WithHeaders(new Dictionary<string, string>() { { "X-Requested-With", "XMLHttpRequest" }, }) // 设置请求头 // .AjaxRequest(true) // 设置 Referer,在做爬虫时会比较有用,还可以通过 WithProxy("proxyUrl") 设置代理 .WithReferer("https://accounting.weihanli.xyz/Account/Login?ReturnUrl=%2F") // 手动设置 UserAgent,默认会随机设置一个 UA .WithUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36") .WithFormParameters(new Dictionary<string, string>() { {
                                "Username","liweihan" }, {
                                  "Password", "112233" }, {
                                    "RememberMe","false" } }) // 设置 post 的 form 参数 // 获取返回的 responseText,并 json 反序列化为一个强类型的Model .Execute<WeihanLi.Common.Models.JsonResultModel<bool>>(); System.Console.WriteLine(loginResult.ToJson()); // 上传文件示例 var uploadFileResponse = new HttpRequester("https://graph.baidu.com/upload", HttpMethod.Post) .WithFile([email protected]"{System.Environment.GetEnvironmentVariable("USERPROFILE")}\Pictures\4e6ab53e383863ed4d15252039f70423.jpg", "image", new Dictionary<string, string>() { { "tn","pc" }, { "from","pc" }, { "image_source","PC_UPLOAD_SEARCH_FILE" }, { "range","{\"page_from\": \"searchIndex\"}" }, }) // 设置上传文件,并设置其它 form 参数信息 .WithReferer("https://baidu.com/") // 设置 referer .WithUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36") .ExecuteForResponse(); // 获取一个 HttpWebResponse 对象,可以使用 StatusCode/ ResponseHeader 等信息 System.Console.WriteLine($"Response status:{uploadFileResponse.StatusCode}, result:{uploadFileResponse.ReadToEnd()}");

More#

除了 Header/Referer/UserAgent 之外,还可以设置 Proxy,设置 Cookie,Ajax 请求 等信息,而且还可以直接 PostJson 示例如下:

Copy
new HttpRequester("requestUrl", HttpMethod.Post)
  .WithProxy("proxyUrl") // 使用代理 //.WithProxy("url", "userName", "password") // 配置带密码的代理 .WithCookie(cookie) //带 Cookie 访问 //.WithCookie("url", cookie) // 只用指定路径的 cookie .WithJsonParameter(entity) // post 一个 json 对象,content-type 会自动设置为 `application/json` .AjaxRequest(true) // 设置该请求是 Ajax 请求 .Execute();

Memo#





C#文件流。

 

什么是文件流:

字节流,把大文件分散成byte[ ] 

使用文件流:

复制代码
class Program
    {      
        static void Main(string[] args)
        {
            //文件流的使用方式。
            string msg = "张三";
            //字符串转成byte数组。
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(msg);
            //byte转成字符串
            string newmsg = System.Text.Encoding.UTF8.GetString(bytes);
            Console.WriteLine(newmsg);

        }    
    }
复制代码

通过FileStream来读写文件。

 

复制代码
 class Program
    {      
        static void Main(string[] args)
        {
            1、 创建文件流
            //FileStream fsWrite = new FileStream("fist.txt", FileMode.Create);
            2、使用文件流,执行读写操作。
            //string msg = "I am a colder.";
            //byte[] bytes = System.Text.Encoding.UTF8.GetBytes(msg);
            写
            参数1:将指定字节数组写入文件
            参数2:参数1的偏移量,一般为0
            参数3:要写入的实际字节个数
            //fsWrite.Write(bytes, 0, bytes.Length);
            3、清空缓冲区  执行上面的操作后  并没有真正的写入而是还在缓冲区(默认满了才写) 
               关闭文件流  
               释放资源   可以只掉这个  它内部会调用上两个
            
            //fsWrite.Flush();
            //fsWrite.Close();
            //fsWrite.Dispose();
            //Console.WriteLine("finished!");
            使用文件流时,可以将代码放到
            using ()
            {

            }
            中 ,可以自动释放资源。



            //读取文件
            //1、创建文件流
            using (FileStream fsRead = new FileStream("fist.txt", FileMode.Open))
            {//不需指定编码,因为这是读取字节流,还没转成字符串
                //根据文件的总字节数,创建byte数组,一次性读入
                byte[] bytes1 = new byte[fsRead.Length];
                //2、读取文件
                fsRead.Read(bytes1, 0, bytes1.Length);
                string readmsg = System.Text.Encoding.UTF8.GetString(bytes1);
                Console.WriteLine(readmsg);
            }


        }    
    }
    
复制代码

 文件流实现大文件拷贝:

复制代码
class Program
    {      
        static void Main(string[] args)
        {
            //文件流实现文件拷贝
            string source = @"c:\copy.txt";
            string [email protected]"e:\copy.txt";
            Copy(source, target);
            
        }

        private static void Copy(string source, string target)
        {
            // 1、创建读取源文件的文件流
            using(FileStream fsRead=new FileStream(source, FileMode.Open, FileAccess.Read))
            {
                //2,创建写入文件的文件流。
                using(FileStream fsWrite=new FileStream(target, FileMode.Create, FileAccess.Write))
                {
                    //拷贝文件的时候,创建一个中间缓冲区。
                    byte[] bytes = new byte[1024];
                    //返回值表示本次实际读取到的字节个数。
                     int r= fsRead.Read(bytes, 0, bytes.Length);
                    while (r>0)
                    {
                        //将读取的内容写到新文件中。 第三个参数应该是实际读取到的字节数
                        fsWrite.Write(bytes, 0, r);
                        Console.WriteLine(".");
                        r = fsRead.Read(bytes, 0, bytes.Length);
                    }

                }
                
            }
        }
    }
复制代码

 File的一些读文件方法  和FileStream    都是一次性读入





Url的Base64编码以及解码

 

Base64可以将二进制转码成可见字符方便进行http传输,但是base64转码时会生成“+”,“/”,“=”这些被URL进行转码的特殊字符,导致两方面数据不一致。我们可以在发送前将“+”,“/”,“=”替换成URL不会转码的字符,接收到数据后,再将这些字符替换回去,再进行解码。在ASP.NET应用程序中,可以使用HttpUtility工具类结合Convert类来实现对URl进行Base64编码以及解码操作。

(1)将URL进行Base64编码

复制代码
    public static string Base64Encrypt(string sourthUrl)
        {
            string eurl = HttpUtility.UrlEncode(sourthUrl);
            eurl = Convert.ToBase64String(encoding.GetBytes(eurl));
            return eurl;
        }
复制代码

(2)将URL进行Base64解码

复制代码
    public static string Base64Decrypt(string eStr)
        {        
            if (!IsBase64(eStr))
            {
                return eStr;
            }
            byte[] buffer = Convert.FromBase64String(eStr);
            string sourthUrl = encoding.GetString(buffer);
            sourthUrl = HttpUtility.UrlDecode(sourthUrl);
            return sourthUrl;
        }
复制代码

 





C#计算字符串长度,汉字算两个字符

 

在C#中的字符串类String中,有个Length属性表示字符串的长度,但该字段返回的是字符的个数,如果字符串中含有中文字符的话,一个汉字占用两个字符的长度,此时获取的长度就不够精确,当然也看具体业务需要。以下方法可用于计算字符串长度,字符串中的一个汉字计为两个字符。

复制代码
/// <summary>
        /// 得到字符串长度,一个汉字长度为2
        /// </summary>
        /// <param name="inputString">参数字符串</param>
        /// <returns></returns>
        public static int StrLength(string inputString)
        {
            System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding();
            int tempLen = 0;
            byte[] s = ascii.GetBytes(inputString);
            for (int i = 0; i < s.Length; i++)
            {
                if ((int)s[i] == 63)
                    tempLen += 2;
                else
                    tempLen += 1;
            }
            return tempLen;
        }
复制代码

 



2019周笔记(2.18-2.23)

 

这周没有什么太专题的东西,就流水账记一下。最近开始入手转型Asp.net MVC,虽然已经落后时代几条街,但是还是赶赶追追。有一些太细的点记在我的笔记里,就不记到这里了,每次发博客园都是第二遍编写,希望能增加记忆。

 

 


2019.02.19
以前都是在C#程序中直接生成MD5,这一次由于逻辑需要,要在SQL 直接生成MD5,学习了一个语句:
select substring(sys.fn_sqlvarbasetostr(HashBytes('MD5','123456')),3,32)
然后进行了发散得到以下知识:
1、--HashBytes ('加密方式', '待加密的值')--(有个疑问,为什么他的返回类型是二进制,最终select出来的确实0x开头的16进制???)
--加密方式= MD2 | MD4 | MD5 | SHA | SHA1 
--返回值类型:varbinary(maximum 8000 bytes)
select HashBytes('MD5','123456')
--HashBytes生成的结果为:0xE10ADC3949BA59ABBE56E057F20F883E

其中,'待加密的值'要非常小心,赋的值相同,如果类型不同,得到的md5相差甚远。例如:varchar(10),nvarchar(10),因为他们的实际存储大小不一样。

2、sys.fn_sqlvarbasetostr用于把字节流类型varbinary,转化成字符流varchar
3、substring('原始字符串','开始位置','截取长度')字符串截取。有一点需要注意的是,sql跟C#程序不同的是,开始位置是从1开始的,不是0,别算错了

2019.02.20
通过MVC的一个[ValidateAntiForgeryToken]特性学习到了“跨网站请求伪造”(CSRF(Cross-site request forgery))这个概念,意思就是第三方站点利用漏洞站点对浏览器cookie的信任,发送虚假请求,从而做出一些未经用户许可的操作,前提是用户未退出信任站点的会话,并且信任站点在本地产生了cookie。之前确实没怎么注意这个细节,只是现在开发的WebAPI项目中根本没有用到cookie,连session也没用,所以应该是安全的。

2019.02.21
集合类型的几种选择思路:
1.如果你返回的集合是只用于遍历,不可修改的,则返回IEnumerable<T>
2.如果返回的集合需要修改,如添加和删除元素,用ICollection<T>
3.如果返回的集合需要支持排序,索引等,用IList<T>
4.如果返回的集合要支持索引,但不能添加,删除元素,用ReadOnlyCollection<T>

2019.02.22
1、泛型:Func<T, bool>,Expression<Func<T, bool>>用法
Func<T, bool>:.net系统自定义了两种委托,有返回值的Func,无返回值的Action。而Func的最后一个参数总是委托的返回类型。
Expression<Func<T, bool>>:是一种表达式,EF中where要求的类型
例如:

复制代码
//正确的代码
Expression<Func<QuestionFeed, bool>> predicate=null;
if (type == 1)
{
  predicate = f => f.FeedID == id && f.IsActive == true;
}
else
{
  predicate = f => f.FeedID == id;
}
_questionFeedRepository.Entities.Where(predicate);
复制代码

 




Mysql语句中当前时间不能直接使用C#中的Date.Now传输

MySql中处理字符串时间,会默认把第一个数字当成年份处理。

在C#服务器中,使用Date.Now.ToString()生成的字符串时间,如果不指定字符串格式,C#会按照系统语言输出不同的字符串格式,如:

a. 美国: 06/01/2019 01:59:00 PM

b.中国: 2019/06/01 13:59:00

 

原因分析:进过翻阅很多资料,汇总一下,发现,Date.Now默认转字符串时跟一个类有关:System.Globalization.CultureInfo(提供有关特定区域性(对于非托管代码开发,则称为“区域设置”)的信息。 这些信息包括区域性的名称、书写系统、使用的日历、字符串的排序顺序以及对日期和数字的格式化设置。)

参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.globalization.cultureinfo?view=netframework-4.7.2

区域语言

而微软通过这个类,对不同系统的用户做了人性化的处理,如:对时间的显示格式存在差别。设置后对服务器代码部署到不同地区的开发者来说,便于对日期等格式的统一化管理!

 

解决方案:

1-临时修改线程中的区域语言为中文模式:

System.Threading.Thread.CurrentThread.CurrentCulture=new System.Globalization.CultureInfo("zh-CN");

2-使用全局配置模式:globalization节点下添加属性:culture="zh-CN" uiCulture="zh-CN"

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <globalization requestEncoding="utf-8" responseEncoding="utf-8" fileEncoding="utf-8" culture="zh-CN" uiCulture="zh-CN" />
  </system.web>

3-Date.Now.ToString("yyyy-MM-dd HH:mm:ss") 指定日期类型,避免使用:string.format(@"{0}",Date.Now)或者Date.Now.ToString()这样的默认字符串格式。

Date.Now.ToString("yyyy-MM-dd HH:mm:ss")

4-使用Mysql自带的获取当前时间方法: now()

select now()  --获取当前时间

 

总结:需要注意的事,前面2中解决方案解决了问题根源;后2中方案只是绕过了这个问题,但是对于后来的新同学可能还会犯同样的错误,所以推荐前面两种方案,这样在写当前时间的时候,你随意怎么写。

另外像:月/日/年 小时:分   这样的时间格式,SqlServer是支持的,这里给SqlServer的强大点个赞!!!

 

备注几个本人查阅很多资料的地址,或许对大家有其他参考价值:

https://docs.microsoft.com/zh-cn/dotnet/api/system.datetime?redirectedfrom=MSDN&view=netframework-4.7.2

https://www.c-sharpcorner.com/article/datetime-in-c-sharp/

 

错误实例一:如下面的代码,在英文版的windows系统下,会导致查询数据不准:应改为:Date.Now.ToString("yyyy-MM-dd HH:mm")

复制代码
/// <summary>
            /// (用户ID) => 用户优惠码可用数量
            /// </summary>
            /// <param name="userId">用户ID</param>
            /// <returns></returns>
            public static int GetCouponCount(int userId)
            {
                int count = 0;
                if (userId > 0)
                {
                    List<string> whereList = new List<string>
                    {
                        string.Format("`{0}` = '{1}'", Coupon_user_mapping._USERID_, userId),
                        string.Format("`{0}` = '{1}'", Coupon_user_mapping._STATUS_, ECoupon.Status.可用.GetValue()),
                        string.Format("`{0}` > '{1}'", Coupon_user_mapping._USEENDTIME_, DateTime.Now)
                    };
                    string where = string.Join(" AND ", whereList);
                    whereList.Clear();
                    whereList = null;
                    Coupon_user_mappingBLL.Select(where, out count);
                }
                return count;
            }
复制代码

 

下面是本公司服务器时间测试截图:左侧为中文windows系统,右侧为英文版windows系统。   在设置CultureInfo区域语言后,英文的系统展示时间也可以为左侧的标准格式了。

 






Mysql中Count函数的正确使用

备注: 直接使用Count(*)或Count(1)这些大家基本都会,主要是Count函数还可以加满足表达式的统计:express


关于Count函数表达式的用法,目前个人只知道2种:

a:使用:Count(表达式 Or null)

b:使用:Count(Case when 表达式 then 1 END) 或者 Count(CASE WHEN 表达式 THEN 1 ELSE null END)

如:

select userid,
COUNT(ParentID=0 OR NULL),
COUNT(CASE WHEN ParentID=0 THEN 1 END),
COUNT(CASE WHEN ParentID=0 THEN 1 ELSE NULL END)
from usermessage
where userid in (511946,656015,1541157,3465768,5049530,5112483,5210074,5372797) GROUP BY userid

 

 

转载于:https://www.cnblogs.com/cjm123/p/10549123.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/anmei1912/article/details/101614483

智能推荐

使用Assembly打包Jar,可直接使用_此处一淌水的博客-程序员秘密_assembly jar

Pom文件 &lt;build&gt; &lt;resources&gt; &lt;resource&gt; &lt;directory&gt;${project.basedir}/src/main/resources&lt;/directory&gt; &lt;filtering&gt;true&lt;/filtering&gt; &lt;excludes&gt;

Pycharm和Python关系_CC_Lsh的博客-程序员秘密_pycharm与python的关系

Pycharm和Python关系简单来说:Pycharm是一个代码编辑器,是目前最流行的代码编辑器之一,用于编写python代码。Python是一个代码解释器,用于将Python代码翻译成计算机可以理解的指令。Pycharm下载地址:PyCharm: the Python IDE for Professional Developers by JetBrainsThe Python &amp; Django IDE with intellig...

pycharm怎样编写java_Pycharm改进和编写代码_伊利心情的博客-程序员秘密

PyCharm包含用于编写代码的各种标准,其中包含适用于Python的适当缩进。 这有助于提高代码标准并在PyCharm编辑器中编写完整的代码。改进代码完成PyCharm中的代码完成非常独特。 您可以使用许多其他功能进一步增强它。 请注意,编辑器提供了代码块的开始和结束。 以下代码编写一个名为demo.py的文件中 -message = 'GIEWIVrGMTLIVrHIQS' #encrypte...

STM32F207(4) 上电关中断_烂笔_头的博客-程序员秘密

环境:STM32F207 内容:上电关闭中断前面我们又提到过,设置时钟的时候我们关闭了一次中断,但是请注意,这里的中断并不是什么定时器啊,外部中断什么的,这个只是针对于时钟树摄制过程中产生的针对于时钟相关的终端。所以这个和我们平时用的中断没有一毛钱关系,真正上电关中断是使用下面代码实现的:INT32S main(void){ // SystemInit(void) CPU_

台达HMI 笔记_Knight_Chester_Sun的博客-程序员秘密

1.需要注意的是,screen edit软件编的程序能用DOP SOFT 打开,但是DOPSOFT打开后再保存,文件就会变成 .DPS格式,无法再用Scredit打开。2. HMI TAGScredit里面的数据代号里面是可以使用DM的bit的,比如DM1023.01, 但是再次点击进去时就会出现数据类型又变成了word型,没关系,忽略就行。DOPSOFT的数据代号里面已经增加

Array_diyingshou8608的博客-程序员秘密

数组数组使应用最广泛的数据存储结构。它被植入到大部分编程语言中 用我的理解来说说数组吧。就行胡萝卜填坑一样,事先准备好坑,然后来一个填一个。拔掉随便一个胡萝卜,则要将拔掉的那个胡萝卜的后面的所...

随便推点

删除chrome浏览器记住密码input自动填充背景色_DenggLin的博客-程序员秘密

/* Change the white to any color ;) */@-webkit-keyframes autofill { to { color: #333; //input中文字的颜色 background: transparent; }}.login-input input:-webkit-autofill { ...

芯片如何储存信息_简单通俗谈信息储存原理_weixin_39986027的博客-程序员秘密

简单通俗谈信息储存原理:网上有个塑料杯留声机的实验,即人对着纸杯喊话,纸杯底部连接一根钢针,钢针的另一头刻在塑料杯上,塑料杯安装在一个滚轴上。即喊话的声音频率刻在滚动的塑料杯上,然后回放滚动的塑料杯就可以还原原声。其实它这个也就是信息储存原理:1,刻下或保存信息能量频率,也即刻下或保存信息。2,刻下,记下,保存的信息能量频率可以振动回放,回放即输出原声。那我们人脑和电脑其实信息储存也是这个原理。人...

(数据结构)图的邻接表存储结构_是我来晚了!的博客-程序员秘密_图的邻接表存储

图的邻接表存储结构一般来说,图更多的是采用链表存储,具体的存储方法有 3 种,分别是邻接表、邻接多重表和十字链表本篇文章将优先介绍邻接表!!!邻接点:在图中,如果两个点相互连通,且通过其中一个顶点,可直接找到另一个顶点,则称它们互为邻接点邻接:指图中顶点之间有边或者弧的存在邻接表存储图的实现方式:给图中的各个顶点独自建立一个链表,用节点存储该顶点,用另一个链表中的节点存储其邻接点特殊之处是,为了便于管理这些链表,通常会将链表的头节点存储到数组中,也正因为各个链表的头节点存储的是各个顶

教你一招解决pycharm启动慢、卡顿的问题,亲测有效_凯旋.Lau的博客-程序员秘密_pycharm启动慢

本文介绍了如何通过给PyCharm分配更多的可用内存来解决PyCharm启动慢、有卡顿的问题,加速PyCharm的启动,避免卡顿。

RxDataSources与TableView实现界面展示(一)_发烧的小龙虾的博客-程序员秘密

// ViewController.swift// RxSwiftTest//// Created by travey on 2018/11/5.// Copyright 2018年 ZhouShijie. All rights reserved.import UIKitimport RxSwiftimport RxCocoaimport SnapKitimp...

软件学院天梯赛参赛队员第一次训练 L2-1 红豆生南国 (25 分)(完全二叉树,树的遍历,重建二叉树,DFS)_forget……的博客-程序员秘密

题目:有诗云: 相思 (王维 唐)红豆生南国, 春来发几枝。愿君多采撷, 此物最相思。那么,我们来采红豆吧!假设红豆树是这个样子的:这种红豆树的特点是:每个结点都有一个正整数编号,标在结点内部。结点的编号各不相同。 最上方一层结点是“红豆”(图中红圈所示的5个结点),这一层被称之为红豆层。 树的根结点、左子结点、右子结点、左子树、右子树等的定义与“数据结构”中的“二叉树”相同,但它毕竟是“自然界中的树”,树根在最下方,如图中的结点5 图中这棵红豆树...