精华内容
下载资源
问答
  • C#中static关键字如何使用,在什么情况下使用? 一个月以前在上java课程的时候,老师讲到了static的基础用法,但是后来忘了,昨天突然想到了,就查阅了大量的资料,发现有些资料讲的就特别啰嗦,所以就讲各种各样的...

    C#中static关键字如何使用,在什么情况下使用?

    一个月以前在上java课程的时候,老师讲到了static的基础用法,但是后来忘了,昨天突然想到了,就查阅了大量的资料,发现有些资料讲的就特别啰嗦,所以就讲各种各样的说法试了一下,总结出了这篇含金量比较高的文章,希望会对您有一些帮助。

    一、先来了解static的中文意思

    在这里插入图片描述
    从上图可以看出,static是静止的,不动的意思

    二、在C#中的意思

    ​ static是静态的的意思,既然是静态的,就是不变的意思,而不变就是它不会因为类中实例化对象的不同而不同,它在类中永远只有一份就像中国人有许多,但是我们这么多人只有一个国家——中国。

    static可以修饰类、字段、属性以及方法。标记为static的就不用创建实例来调用了,如果外部要对其进行调用,可以直接通过(类名.方法名)来调用,内部就可以直接通过方法名调用。

    三、由static修饰的归属问题

    ​ 被static修饰的不管是变量还是方法,都属于类直接管理,所以类中所有的方法(静态和实例的方法)都可以调用他们。举个例子吧,就不如:一个公司的打印机,它是属于公司公共的物品,而不是说属于哪个部门哪个人的,公司里的所有人员(实例)都有权去使用这个公共的打印机。

    例如:

     class Program1
        {
            static int i;
            static void main() {	//被static修饰的方法,被称为静态方法
                Console.WriteLine(i);
            }
            public  void Ak() {	//Ak是实例方法
                Console.WriteLine("aaaaa");
                main();//实例方法可以调用静态方法main();
            }
        }
    

    四、static修饰的范围

    1、类

    static可以修饰类,使类成为静态类。这样在此类中只能定义静态的方法和静态的变量

    (1)静态类和非静态类的区别

    ​ 静态类和非静态类最重要的区别在于静态类不能被实例化,也就是说不能使用new关键字创建静态类类型的变量。

    ​ 在声明一个类时使用static关键字,具有两方面的意义:第一:它防止程序员写代码来实例化该静态类;第二,它防止在类的内部声明任何实例字段或方法,也就是说静态类中的字段方法必须为静态的而不是实例的。

    (2)静态类的特征

    • 静态类中只能包含静态成员,其他实例成员一律不行
      在这里插入图片描述
    • 静态类无法通过new关键字的方法实例化
      在这里插入图片描述
    • 静态类的本质,是一个抽象的密封类,所以不能作为基类用来继承
    • 不能包含实例构造函数
    • 如果一个类下的所有成员都需要被共享,就是供其他类使用,那么就可以把这个类定义为静态类

    2、变量

    static可以修饰变量,注意:这个变量只能是成员变量,而不能是局部变量

    静态成员变量的特点:

    • 静态成员变量是属于类的,类被加载时初始化,并且有默认初始值(实例成员变量属于对象,在每个对象被创建时初始化,每个对象一份)
    • 存在优先于对象,被所有对象所共享,常驻内存

    3、方法

    static可以修饰方法,Main方法必须用static修饰,因为它是与程序共存亡的,是程序的入口和程序的大门

    静态方法的特点:

    • 静态方法是不属于特定对象的方法
    • 静态方法可以直接访问静态成员(包括静态字段、静态方法)
    • 静态方法不可以直接访问实例成员,并且静态方法也不能直接调用实例方法

    例子1:

     class Program1
        {
            int i;//正确,成员变量有默认初始值
            public void Bb(/*tatic string str */)//错误,形式参数不能使用static修饰
            {
               // int c;//错误,局部变量需要手动赋值,才能使用
                //static int a;//错误,局部变量不能使用static修饰
            }
        }
    

    例子2:

     static class Program1
        {
            int i;//错误,静态类中只能定义静态成员变量
            public void Bb()//错误,静态类中只能定义静态成员方法
            {
            }
        }
    

    4、构造函数

    static可以修饰,使构造函数成为静态构造函数。并且不能在再使用其他修饰符,并且不能是实例构造函数

    静态构造函数的特点:

    • 静态类可以有静态构造函数,也就是说静态类中的构造函数只能是静态的,并且不可继承
    • 静态构造函数没有访问修饰符,没有参数,只有一个static修饰符
    • 在非静态类中也可以有静态构造函数,并且,静态构造函数在,非静态类在实例化之前要被自动执行,并且速度要比实例化后调用的构造函数要快
    • 静态构造函数只执行一次

    例子:

      class Program1
        {
           static Program1()//静态构造函数
            {
                Console.WriteLine("静态构造函数被执行");
            }
            public Program1()//非静态构造函数
            {
                Console.WriteLine("非态构造函数被执行");
            }
        }
    //外部调用
      static void Main(string[] args)
            {
              
                Program1 p = new Program1();
                Console.ReadLine();
            }
    

    输出结果:
    在这里插入图片描述

    五、static修饰的调用

    1、在本类里调用由static修饰的方法和变量,可以直接使用

      class Program1
        {
            static int i;//定义静态成员变量
            static public void Bb()//定义静态成员方法
            {
                Console.WriteLine(i);
                main();
            }
            static void main()
            {
                Console.WriteLine("我是成员方法main");
            }
        }
    

    2、在外部类中调用static修饰的方法和变量,需要(类名.方法名或变量名)

    //本类中
      class Program1
        {
            static public int i;//定义静态成员变量
            static public void Bb()//定义静态成员方法
           	 {
                Console.WriteLine(i);
             }
            static public void main()
           	 {
                Console.WriteLine("我是成员方法main");
             }
        }
        //外部类调用
          class Program
        {
            static int i;
            static void Main(string[] args)
            {
                //直接类名.方法名或者变量名调用
                Console.WriteLine(Program1.i);
                Program1.main();
                Program1.Bb();
            }
            
        }
    

    六、什么情况下使用static修饰

    • 当变量需要共享时,可以将变量定义为静态变量
    • 当方法需要被反复调用时,可以将方法定义为静态方法
    • 当一个类中包含的成员都是静态时,可以将类定义为静态类

    本篇文章详细讲解了static修饰符的用法,希望对各位朋友有所帮助。也许有些朋友会觉得我总结的不太完善或者有不对的地方,还请各位朋友能够指点迷津,指出我的错误,不要让我恶性循环了,哈哈哈,就跟大家说到这里吧,我会不定时更新博文的,还请各位朋友能多多支持,谢谢大家!

    展开全文
  • 我看到C ++中有多种分配和释放数据的方法,并且我了解,当调用malloc应该调用free而当您使用new运算符时,应该与delete配对,并且将两者混用是错误的(例如,调用free()

    我看到在C ++中有多种分配和释放数据的方法,并且我了解,当您调用malloc您应该调用free而当您使用new运算符时,您应该与delete配对,并且将两者混用是错误的(例如,调用free()在使用new运算符创建的内容上),但是我不清楚在实际程序中何时应使用malloc / free以及何时应使用new / delete

    如果您是C ++专家,请让我知道您在这方面遵循的经验法则或惯例。


    #1楼

    malloc()用于动态分配C中的内存,而c ++中的new()完成相同的工作。 因此,您不能混合使用两种语言的编码约定。 如果您要求calloc和malloc()之间的区别会很好


    #2楼

    在以下情况下,我们不能使用new,因为它调用了构造函数。

    class  B  {
    private:
        B *ptr;
        int x;
    public:
        B(int n)  {
            cout<<"B: ctr"<<endl;
            //ptr = new B;  //keep calling ctr, result is segmentation fault
            ptr = (B *)malloc(sizeof(B));
            x = n;
            ptr->x = n + 10;
        }
        ~B()  {
            //delete ptr;
            free(ptr);
            cout<<"B: dtr"<<endl;
        }
    };
    

    #3楼

    如果您使用不需要构造/销毁并且需要重新分配的数据(例如,大量的int),那么我相信malloc / free是一个不错的选择,因为它可以为您提供重新分配,这比new-memcpy更快-delete(在我的Linux机器上,但是我想这可能取决于平台)。 如果使用不是POD且需要构造/销毁的C ++对象,则必须使用new和delete运算符。

    无论如何,我不明白为什么不应该同时使用两种方法(前提是您释放了已分配的内存并删除了用new分配的对象),如果可以利用速度提升的优势(如果您要重新分配大型数组,有时会很重要)重新分配可以给您的POD)。

    除非您需要,否则应坚持使用C ++中的new / delete。


    #4楼

    除非您被迫使用C,否则永远不要使用 malloc 。 始终使用new

    如果您需要大量数据,请执行以下操作:

    char *pBuffer = new char[1024];
    

    请注意,尽管这是不正确的:

    //This is incorrect - may delete only one element, may corrupt the heap, or worse...
    delete pBuffer;
    

    相反,您应该在删除数据数组时执行此操作:

    //This deletes all items in the array
    delete[] pBuffer;
    

    new关键字是C ++的实现方式,它将确保您的类型将其构造函数称为new关键字也是更类型安全的,malloc根本不是类型安全的。

    我认为使用malloc的唯一方法就是需要更改数据缓冲区的大小new关键字没有类似realloc的类似方式。 realloc函数可以为您更有效地扩展内存块的大小。

    值得一提的是,您不能将new / freemalloc / delete

    注意:此问题的某些答案无效。

    int* p_scalar = new int(5);  // Does not create 5 elements, but initializes to 5
    int* p_array  = new int[5];  // Creates 5 elements
    

    #5楼

    mallocfree用于分配将由以c为中心的库和API管理的内存。 对您控制的所有内容使用newdelete (以及[]变体)。


    #6楼

    如果您有C代码,想移植到C ++,则可以在其中保留所有malloc()调用。 对于任何新的C ++代码,我建议改用new。


    #7楼

    始终在C ++中使用new。 如果需要一块无类型的内存,则可以直接使用运算符new:

    void *p = operator new(size);
       ...
    operator delete(p);
    

    #8楼

    C ++ FQA Lite

    [16.4]为什么我应该使用new而不是值得信赖的旧malloc()?

    常见问题解答:new / delete调用构造函数/析构函数; new是类型安全的,malloc不是; new可以被类覆盖。

    FQA:FAQ中提到的new的优点不是优点,因为构造函数,析构函数和运算符重载是垃圾(请参阅没有垃圾回收时会发生什么?),并且类型安全问题在这里确实很小(通常您有强制将malloc返回的void *转换为正确的指针类型,以将其分配给类型化的指针变量,这可能很烦人,但远非“不安全”)。

    哦,使用值得信赖的旧版malloc可以使用同样值得信赖的旧版realloc。 不幸的是,我们没有新的运营商续订或其他新的东西。

    尽管如此,即使语言是C ++,new仍不足以证明偏离一种语言所使用的通用样式。 特别是,如果您简单地分配对象,则具有非平凡构造函数的类将以致命的方式行为异常。 那么,为什么不在整个代码中使用new? 人们很少重载运算符new,因此它可能不会过多地妨碍您。 如果它们确实超载了,您可以随时要求它们停止。

    抱歉,我无法抗拒。 :)


    #9楼

    newdelete运算符可以对类和结构进行操作,而mallocfree仅适用于需要转换的内存块。

    使用new/delete将有助于改善代码,因为您无需将分配的内存转换为所需的数据结构。


    #10楼

    malloc并没有提供一些new功能:

    1. new通过调用对象的构造函数来构造该对象
    2. new不需要类型化分配的内存。
    3. 它不需要分配大量的内存,而是需要构造许多对象。

    因此,如果使用malloc ,则需要显式地执行上述操作,这并不总是可行的。 另外, new可以重载,但malloc不能。


    #11楼

    如果使用的是C ++,请尝试使用new / delete代替malloc / calloc,因为它们是运算符。 对于malloc / calloc,您需要包括另一个头。 不要在同一代码中混用两种不同的语言。 他们的工作在每种方式上都是相似的,都从哈希表中的堆段中动态分配内存。


    #12楼

    新vs malloc()

    1) new是一个运算符 ,而malloc()是一个函数

    2) new调用了构造函数 ,而malloc()没有。

    3) new返回确切的数据类型 ,而malloc()返回void *

    4) new从不返回NULL (将引发失败),而malloc()返回NULL

    5)重新分配未由new处理的内存,而malloc()可以


    #13楼

    new将初始化该结构的默认值,并将其中的引用正确链接到其自身。

    例如

    struct test_s {
        int some_strange_name = 1;
        int &easy = some_strange_name;
    }
    

    因此, new struct test_s将返回带有工作引用的初始化结构,而malloc的版本没有默认值,并且未初始化内部引用。


    #14楼

    很少考虑使用malloc / free而不是new / delete是在使用realloc进行分配然后重新分配(简单的pod类型,不是对象)时,因为在C ++中没有类似的函数可以重新分配(尽管可以使用a更多的C ++方法)。


    #15楼

    要回答您的问题,您应该知道mallocnew之间的区别 。 区别很简单:

    malloc 分配内存 ,而new 分配内存并调用要为其分配内存的对象的构造函数

    因此,除非限于C语言,否则永远不要使用malloc,尤其是在处理C ++对象时。 那将是破坏程序的秘诀。

    另外freedelete之间的区别是相同的。 区别在于, delete除了释放内存外,还将调用对象的析构函数。


    #16楼

    仅当对象的生存期与创建对象的作用域不同时才需要动态分配(这同样适用于使作用域变小或变大),并且您有特定的原因不按值存储它工作。

    例如:

     std::vector<int> *createVector(); // Bad
     std::vector<int> createVector();  // Good
    
     auto v = new std::vector<int>(); // Bad
     auto result = calculate(/*optional output = */ v);
     auto v = std::vector<int>(); // Good
     auto result = calculate(/*optional output = */ &v);
    

    从C ++ 11开始,我们有std::unique_ptr用于处理分配的内存,其中包含分配的内存的所有权。 std::shared_ptr是为您必须共享所有权而创建的。 (您所需要的比您在一个好的程序中所期望的要少)

    创建实例变得非常容易:

    auto instance = std::make_unique<Class>(/*args*/); // C++14
    auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
    auto instance = std::make_unique<Class[]>(42); // C++14
    auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
    

    C ++ 17还添加了std::optional ,可以防止您需要内存分配

    auto optInstance = std::optional<Class>{};
    if (condition)
        optInstance = Class{};
    

    一旦“实例”超出范围,内存就会被清理。 转让所有权也很容易:

     auto vector = std::vector<std::unique_ptr<Interface>>{};
     auto instance = std::make_unique<Class>();
     vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)
    

    那么什么时候还需要new呢? 从C ++ 11开始几乎没有。 大多数情况下,您都使用std::make_unique直到达到击中通过原始指针转移所有权的API的地步。

     auto instance = std::make_unique<Class>();
     legacyFunction(instance.release()); // Ownership being transferred
    
     auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
    

    在C ++ 98/03中,您必须执行手动内存管理。 如果是这种情况,请尝试升级到该标准的最新版本。 如果您被卡住:

     auto instance = new Class(); // Allocate memory
     delete instance;             // Deallocate
     auto instances = new Class[42](); // Allocate memory
     delete[] instances;               // Deallocate
    

    确保正确跟踪所有权,以确保没有任何内存泄漏! 移动语义也不起作用。

    那么,什么时候我们需要C ++中的malloc? 唯一有效的原因是分配内存,然后稍后通过放置new对其进行初始化。

     auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
     auto instance = new(instanceBlob)Class{}; // Initialize via constructor
     instance.~Class(); // Destroy via destructor
     std::free(instanceBlob); // Deallocate the memory
    

    即使上述内容是有效的,也可以通过new-operator来完成。 std::vector就是一个很好的例子。

    最后,我们仍然在房间里放大象: C 。 如果必须使用C库来在C ++代码中分配内存,并在C代码中释放内存(或者相反),则必须使用malloc / free。

    如果是这种情况,请忽略虚拟函数,成员函数,类……仅允许其中包含POD的结构。

    规则的一些例外:

    • 您正在使用适当的malloc编写具有高级数据结构的标准库
    • 您必须分配大量内存(在10GB文件的内存副本中?)
    • 您的工具阻止了您使用某些构造
    • 您需要存储不完整的类型

    #17楼

    从较低的角度看,new将在提供内存之前初始化所有内存,而malloc将保留内存的原始内容。


    #18楼

    简短的答案是:在没有充分理由的情况下,请勿将malloc用于C ++。 与C ++一起使用时, malloc有许多缺陷,这些缺陷是new定义的。

    C ++代码的新增功能修复的缺陷

    1. malloc在任何有意义的方式上都不是类型安全的。 在C ++中,您需要强制返回void*的返回值。 这可能会带来很多问题:

       #include <stdlib.h> struct foo { double d[5]; }; int main() { foo *f1 = malloc(1); // error, no cast foo *f2 = static_cast<foo*>(malloc(sizeof(foo))); foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad } 
    2. 比这更糟。 如果所讨论的类型是POD(普通旧数据),则可以像第一个示例中的f2一样,半明智地使用malloc为它分配内存。

      如果类型是POD,则不是很明显。 一个重要的因素是给定类型可能从POD更改为非POD,而不会导致编译器错误,并且可能很难调试问题。 例如,如果某人(可能是另一个程序员,在维护期间,后来又进行了更改,导致foo不再是POD),那么在编译时就不会出现您希望的明显错误,例如:

       struct foo { double d[5]; virtual ~foo() { } }; 

      会使f2malloc也变坏,而没有任何明显的诊断。 这里的示例很简单,但是有可能在更远的地方意外地引入了非POD(例如,在基类中,通过添加非POD成员)。 如果您具有C ++ 11 / boost,则可以使用is_pod来检查此假设是否正确,如果不正确,则会产生错误:

       #include <type_traits> #include <stdlib.h> foo *safe_foo_malloc() { static_assert(std::is_pod<foo>::value, "foo must be POD"); return static_cast<foo*>(malloc(sizeof(foo))); } 

      尽管boost 无法确定没有C ++ 11或其他一些编译器扩展的类型是否为POD

    3. 如果分配失败,则malloc返回NULLnew将抛出std::bad_alloc 。 稍后使用NULL指针的行为是不确定的。 抛出异常并从错误源抛出异常时,它具有清晰的语义。 在每次调用时用适当的测试包装malloc看起来很乏味且容易出错。 (您只需忘记一次就可以撤消所有的出色工作)。 可以允许将异常传播到调用者能够明智地对其进行处理的级别,因为NULL更难以有意义地传递回去。 我们可以扩展我们的safe_foo_malloc函数以引发异常或退出程序或调用某些处理程序:

       #include <type_traits> #include <stdlib.h> void my_malloc_failed_handler(); foo *safe_foo_malloc() { static_assert(std::is_pod<foo>::value, "foo must be POD"); foo *mem = static_cast<foo*>(malloc(sizeof(foo))); if (!mem) { my_malloc_failed_handler(); // or throw ... } return mem; } 
    4. 从根本上说, malloc是C的功能,而new是C ++的功能。 结果, malloc在构造malloc中不能很好地发挥作用,它只着眼于分配字节块。 我们可以进一步扩展safe_foo_malloc以使用new放置:

       #include <stdlib.h> #include <new> void my_malloc_failed_handler(); foo *safe_foo_malloc() { void *mem = malloc(sizeof(foo)); if (!mem) { my_malloc_failed_handler(); // or throw ... } return new (mem)foo(); } 
    5. 我们的safe_foo_malloc函数不是很通用-理想情况下,我们希望可以处理任何类型的东西,而不仅仅是foo 。 我们可以使用非默认构造函数的模板和可变参数模板来实现此目的:

       #include <functional> #include <new> #include <stdlib.h> void my_malloc_failed_handler(); template <typename T> struct alloc { template <typename ...Args> static T *safe_malloc(Args&&... args) { void *mem = malloc(sizeof(T)); if (!mem) { my_malloc_failed_handler(); // or throw ... } return new (mem)T(std::forward(args)...); } }; 

      现在,尽管解决了迄今为止我们发现的所有问题,我们实际上已经重新发明了默认的new运算符。 如果要使用mallocnew放置,那么最好还是使用new作为开始!


    #19楼

    mallocnew之间有一个很大的区别。 malloc分配内存。 这对于C很好,因为在C中,内存块是一个对象。

    在C ++中,如果您不处理POD类型(类似于C类型),则必须在内存位置上调用构造函数以实际在其中具有对象。 非POD类型在C ++中非常常见,因为许多C ++功能使对象自动成为非POD对象。

    new分配内存在该内存位置创建一个对象。 对于非POD类型,这意味着调用构造函数。

    如果您执行以下操作:

    non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
    

    您获得的指针不能取消引用,因为它没有指向对象。 您必须先在其上调用构造函数,然后才能使用它(这可以通过放置new来完成)。

    另一方面,如果您这样做:

    non_pod_type* p = new non_pod_type();
    

    您将获得始终有效的指针,因为new创建了一个对象。

    即使对于POD类型,两者之间也存在显着差异:

    pod_type* p = (pod_type*) malloc(sizeof *p);
    std::cout << p->foo;
    

    由于未初始化由malloc创建的POD对象,因此这段代码将输出未指定的值。

    使用new ,您可以指定要调用的构造函数,从而获得定义良好的值。

    pod_type* p = new pod_type();
    std::cout << p->foo; // prints 0
    

    如果确实需要,则可以使用new获取未初始化的POD对象。 有关更多信息,请参见此其他答案

    另一个区别是失败时的行为。 当分配内存失败时, malloc返回一个空指针,而new抛出异常。

    前者要求您在使用它之前测试返回的每个指针,而后者则将始终产生有效的指针。

    由于这些原因,在C ++代码中,您应该使用new而不是malloc 。 但是即使那样,您也不应使用new “公开”方式,因为它获取了以后需要释放的资源。 使用new ,应立即将其结果传递到资源管理类中:

    std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
    
    展开全文
  • 学习了out,总结一下什么情况下使用out返回参数值呢? 当你想让方法返回多个值的时候,就使用out参数,因为一个方法一般只有一个返回值。out是内部为外部变量赋值,out一般用函数需要有多个返回值的场所。   ...

    ---------------------- Windows Phone 7手机开发.Net培训、期待与您交流! ----------------------

    学习了out,总结一下什么情况下使用out返回参数值呢?

    当你想让方法返回多个值的时候,就使用out参数,因为一个方法一般只有一个返回值。out是内部为外部变量赋值,out一般用在函数需要有多个返回值的场所。

     

    实现步骤:

        class Program
        {
            static void Main(string[] args)
            {
                int number;
                int result = Test(out number);
                Console.WriteLine("number={0},result={1}",number,result);
            }

            static int Test(out int a)
            {
                a = 10;
                a = a + 1;
                return 200;
            }

        }

    如上代码:

    1)在方法的参数类型前加out,那么传参数的时候也必须在number前加out表明这个参数不是传入值的,而是用来传出值的;

    2)如果参数是以out形式传入的,那么在传入前可以不赋初值;

    3)在方法中对于由out修饰的参数,必须赋值。并且必须在使用前赋值。

     

    谈到out,也必须谈谈ref,ref和out的用法类似。

    out与ref的区别:

    out用于传出值,ref可以理解为是双向的,既可以传入又可以传出。

     

    在传参数的过程中,如果参数有out或ref修饰的话,那么改变方法中的参数变量的值,调用者方法中的变量的值也会相应的改变。

     

    ---------------------- Windows Phone 7手机开发.Net培训、期待与您交流! ----------------------详细请查看:http://net.itheima.com/
    展开全文
  • 比如的某个表有一个时间列,恰好把 聚合索引建立了该列,这时查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为的这本字典正文是按日期进行 排序的,聚类索引只需要找到要...

    怎么建索引

     

    事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把 聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行 排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到 具体内容。 

     (三)结合实际,谈索引使用的误区 

      理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。 

      1、主键就是聚集索引 

      这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。 

      通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列 Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。 

      显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。 

      从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中, 因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪 费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容, 特别是索引项的时候会负作用,但对于查询速度并没有影响。 

      在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。 

      通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已 建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过 了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段 来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。 

      在这里之所以提到“理论上”三字,是因为如果您的聚集索引还是盲目地建在ID这个主键上时,您的查询速度是没有这么高的,即使您在“日期”这个 字段上建立的索引(非聚合索引)。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现(3个月内的数据为25万条): 

      (1)仅在主键上建立聚集索引,并且不划分时间段: 

      Select gid,fariqi,neibuyonghu,title from tgongwen 

      用时:128470毫秒(即:128秒) 

      (2)在主键上建立聚集索引,在fariq上建立非聚集索引: 

      select gid,fariqi,neibuyonghu,title from Tgongwen 

      where fariqi> dateadd(day,-90,getdate()) 

      用时:53763毫秒(54秒) 

      (3)将聚合索引建立在日期列(fariqi)上: 

      select gid,fariqi,neibuyonghu,title from Tgongwen 

      where fariqi> dateadd(day,-90,getdate()) 

      用时:2423毫秒(2秒) 

      虽然每条语句提取出来的都是25万条数据,各种情况的差异却是巨大的,特别是将聚集索引建立在日期列时的差异。事实上,如果您的数据库真的有 1000万容量的话,把主键建立在ID列上,就像以上的第1、2种情况,在网页上的表现就是超时,根本就无法显示。这也是我摒弃ID列作为聚集索引的一个 最重要的因素。

     
      得出以上速度的方法是:在各个select语句前加: 

      declare @d datetime 

      set @d=getdate() 

      并在select语句后加: 

      select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate()) 

     2、只要建立索引就能显著提高查询速度 

      事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。 

      从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在 现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。 由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。 
     
      3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度 

      上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。 

      很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这 个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在 后列) 

      (1)select gid,fariqi,neibuyonghu,title from Tgongwen 

      where fariqi>'2004-5-5' 

      查询速度:2513毫秒 

      (2)select gid,fariqi,neibuyonghu,title from Tgongwen 

      where fariqi>'2004-5-5' and neibuyonghu='办公室' 

      查询速度:2516毫秒 

      (3)select gid,fariqi,neibuyonghu,title from Tgongwen 

      where neibuyonghu='办公室' 

      查询速度:60280毫秒 

      从以上试验中,我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复 合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的 查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记 住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。 
     
      (四)其他书上没有的索引使用经验总结 

      1、用聚合索引比用不是聚合索引的主键速度快 

      下面是实例语句:(都是提取25万条数据) 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi='2004-9-16' 

      使用时间:3326毫秒 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000 

      使用时间:4470毫秒 

      这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。 

      2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi 

      用时:12936 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid 

      用时:18843 

      这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。 
     
    3、使用聚合索引内的时间段,搜索时间会按数据占整个数据表的百分比成比例减少,而无论聚合索引使用了多少个 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi>'2004-1-1' 

      用时:6343毫秒(提取100万条) 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi>'2004-6-6' 

      用时:3170毫秒(提取50万条) 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi='2004-9-16' 

      用时:3326毫秒(和上句的结果一模一样。如果采集的数量一样,那么用大于号和等于号是一样的) 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi>'2004-1-1' and fariqi<'2004-6-6' 

      用时:3280毫秒 

      4 、日期列不会因为有分秒的输入而减慢查询速度 

      下面的例子中,共有100万条数据,2004年1月1日以后的数据有50万条,但只有两个不同的日期,日期精确到日;之前有数据50万条,有5000个不同的日期,日期精确到秒。 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi>'2004-1-1' order by fariqi 

      用时:6390毫秒 

      select gid,fariqi,neibuyonghu,reader,title from Tgongwen 

      where fariqi<'2004-1-1' order by fariqi 

      用时:6453毫秒 

      (五)其他注意事项 

      “水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。过多的索引甚至会导致索引碎片。 

      索引是从数据库中获取数据的最高效方式之一。95%的数据库性能问题都可以采用索引技术得到解决。 
     1. 不要索引常用的小型表 

      不要为小型数据表设置任何键,假如它们经常有插入和删除操作就更别这样作了。对这些插入和删除操作的索引维护可能比扫描表空间消耗更多的时间。 

      2. 不要把社会保障号码(SSN)或身份证号码(ID)选作键 

      永远都不要使用 SSN 或 ID 作为数据库的键。除了隐私原因以外,SSN 或 ID 需要手工输入。永远不要使用手工输入的键作为主键,因为一旦你输入错误,你唯一能做的就是删除整个记录然后从头开始。 

      3. 不要用用户的键 

      在确定采用什么字段作为表的键的时候,可一定要小心用户将要编辑的字段。通常的情况下不要选择用户可编辑的字段作为键。这样做会迫使你采取以下两个措施: 

      4. 不要索引 memo/notes 字段和不要索引大型文本字段(许多字符) 

      这样做会让你的索引占据大量的数据库空间 

      5. 使用系统生成的主键 

      假如你总是在设计数据库的时候采用系统生成的键作为主键,那么你实际控制了数据库的索引完整性。这样,数据库和非人工机制就有效地控制了对存储数据中每一行的访问。 

      采用系统生成键作为主键还有一个优点:当你拥有一致的键结构时,找到逻辑缺陷很容易。 
     
    转自:https://www.cnblogs.com/lanze/articles/4868262.html

     

    转载于:https://www.cnblogs.com/gered/p/9671827.html

    展开全文
  • 知道在什么情况下的Linux才可以自动获取IP地址?答:(1) Linux中使用 dhclient 命令可以从DHCP服务器上获得一个可用的IP地址(2)首先,需要激活网卡eth0;其次,如果linux是虚拟机,那么还需要设置虚拟机的...
  • 使用任何框架的情况下核心Java中实现自己的轻量级依赖注入。 总览 本文将指导您使用自己的依赖注入实现来理解和构建轻量级Java应用程序。 依赖注入…DI…控制反转…IoC,我想可能常规例行程序或特别的...
  • 间谍相机 这个库是做什么用的? 该库允许应用程序使用设备相机拍摄照片,而不显示其预览。 任何应用程序都可以后台从前置摄像头捕获图像。 演示版 ...其中,必须传递两个参数,第一个是Activity
  • 可以通过CLI使用它,也可以没有客户端的情况下使用curl来使用它。 它也可以用作自托管的NoPaste或临时文件托管服务。 安装pcopy服务器之后,可以使用pcopy命令行工具从STDIN复制( pcp < file> file.txt )。 ...
  • 没有信令服务器的情况下使用WebRTC的演示。 但是对于用Kotlin编写的Android。 由Chris Ball为JavaScript编写的此项目兼容并受其启发: 什么是WebRTC? 这是用于实时对等通信的技术。 对于传输音频和视频-电话...
  • 幸运的是,可以使用外部编译器编译的模组以绕过限制,但默认情况下,游戏不会不重新启动的情况下重新加载的模组。 可以每次重新编译时删除的 mod 二进制文件并更改程序集版本( )以启用热加载,但这...
  • WebP Server这是一个基于 Golang 的服务器,允许动态提供 WebP 图像,不改变图片URL路径的情况下,自动将JPEG、PNG、BMP、GIF等图像转换为WebP格式,从而减小图片体积,降低流量消耗和提高加载速度。 什么是...
  • 如果你要确定文件存在的话然后做些什么,那么使用try是最好不过的 如果不打算立即打开文件,则可以使用os.path.isfile检查文件 如果path是现有常规文件,则返回true。对于相同的路径,islink()和isfile()都可以为...
  • 使用正则表达式解析源文件的问题在于,无法确定匹配项未注释或字符串中。 例如,假设正在寻找Javascript文件中的所有const语句-使用类似于以下内容的正则表达式: / \b const \s + / g 但是,如果要...
  • 相比之下,该应用程序只有一个目标:没有Play服务的情况下运行Gcam。 因此,此应用不需要任何破坏黑客或权限的安全性(不需要Internet访问,不需要存储访问,当然也不需要root用户访问权限)。 如何安装此应用...
  • 测试WordPress插件或主题时,它某些情况下有助于验证当前使用的模板文件。 这个小WordPress插件将当前模板的路径添加到前端页脚中。 先决条件 WordPress安装。 安装-手动上传 从此处下载zip: : WordPress...
  • 什么情况下需要用到git创建分支

    千次阅读 2018-10-21 10:12:35
    我给说一下我使用到分支的情况本地使用分支 本来master分支上开发的,如果我每实现一个小的功能,就进行一次commit的话?那么分支上不就有很多的commit的吗?推送上去,会看见服务器上有很多不必要的提交...
  • 使用imx_usb_loader的情况下在交换机上引导Linux-Windows,Linux,Mac OS和Android 免责声明 如果对交换机,其LCD屏幕或电池造成了损坏或炸毁,我概不负责。 使用此方法需要自担风险,因为众所周知Linux会...
  • YoPlay 是生成器,为 (Scala) + (默认情况下在内存数据库中使用 )创建开发环境。 几分钟后,将获得: 开箱即用的开发环境 适用于实体的非常基本的异步 CRUD - 适用于启动器/模拟后端 你需要什么? - Yeo 是一...
  • 双击一个加密文件以使用它(例如,双击proposal.docx.gpgWord中直接将其打开) 它有什么作用? 它使的文件保持加密状态,同时允许无缝访问以读取/编辑/更新这些文件。 只需Windows资源管理器中双击即可打开...
  • Tina是一个开放源代码工具包,用于直接在您的网站中构建内容管理。 入门 查看该以开始使用TinaCMS。 文献资料 请访问以查看完整的文档。 开发过程 请参阅我们的以了解维护人员的工作方式。 有什么问题吗 访问以提问...
  • LLDebugTool是面向开发人员和测试人员的调试工具,可以帮助您在非xcode情况下分析和处理数据。 是延长 ,它提供LLDebugTool快捷界面,LLDebugToolSwift将LLDebugTool释放在同一时间。 如果您的项目是Objective-C...
  • 这就是为什么我开始开发该项目的原因,其构想是将建筑物和APP结合到任何可穿戴设备(在我的情况下为Xiaomi Mi Band 3)收集的,并与Spotify播放列表存储在Google Fit应用中的心脏BPM上。您在执行任何类型的活动...
  • 由于Google图片搜索API每天最多只能处理100张图片,因此该程序将一段时间后停止运行,这种情况下只需将其关闭并等待一天。 但是,还有一个已知的错误,其中图像无法加载,因此无限期挂起(有时是因为返回的...
  • React脚本是从,但是默认情况下使用作为测试运行,并使用作为测试框架。 为什么 某些情况下,需要真实的浏览器中运行测试,以进行跨浏览器测试,或者需要获得对尚未JSDOM中实现的规范的支持。 使用该项目,...
  • LLDebugTool是面向开发人员和测试人员的调试工具,可以帮助您在非xcode情况下分析和处理数据。 是延长 ,它提供LLDebugTool快捷界面,LLDebugToolSwift将LLDebugTool释放在同一时间。 如果您的项目是Objective-C...
  • 饮食 1.简介 简单的实用程序,可以计算机上分配内存 2.我可以用它做什么? 测试交换 可用内存不足时计算机上测试行为 3.安装 ... eatmemoryDockerhub中,因此无需运行构建过程就可以运行它 $
  • checktypes-js ...还能要求什么? 简单的例子 const checkTypes = require ( 'checktypes-js' ) const aNumber = 33 const [ error , numberPassed ] = checkTypes ( aNumber , Number ) if ( error ) c
  • 允许使用agvtool的情况下设置/获取版本以及执行其他一些小技巧。 需要注意的是所有办法传递给喜欢动作increment_version_number_in_plist , increment_build_number_in_xcodeproj或get_info_plist_path...
  • 对标题感到惊讶?是的,如果不了解HTML,CSS,JavaScript,则可以使用某些Client / Server...Web前端框架的功能是什么:视觉布局IDE中设计,可以浏览器中使用HTML,Javascript和CSS进行呈现。 使用Delphi或
  • 这是一个脚本,可让您在没有root设备的情况下在您的termux应用程序中安装Ubuntu 更新 •更新到ubuntu 20.04 重要的 •如果必须在具有x86 / i * 86架构的termux中使用ubuntu或更喜欢ubuntu 19.10,则可以使用此分支-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,926
精华内容 1,570
关键字:

您在什么情况下使用