পরম পারফরম্যান্সের জন্য, এসইএম দ্রুত বা COUNT?


31

এই রেকর্ডগুলি একটি নির্দিষ্ট শর্ত মেলে, যেমন সংখ্যা বেড়ে চলেছে সম্পর্কিত invoice amount > $100

আমি পছন্দ করি

COUNT(CASE WHEN invoice_amount > 100 THEN 1 END)

তবে এটি ঠিক যেমন বৈধ

SUM(CASE WHEN invoice_amount > 100 THEN 1 ELSE 0 END)

আমি ভাবতাম যে COUNT টি কারণ 2 টির জন্য পছন্দনীয়:

  1. অভিপ্রায়টি জানায়, যা করা COUNT
  2. COUNT সম্ভবতi += 1 কোথাও একটি সহজ অপারেশন জড়িত , SUM একটি সাধারণ পূর্ণসংখ্যার মান হিসাবে তার অভিব্যক্তি উপর নির্ভর করতে পারে না।

কারও কাছে কি নির্দিষ্ট আরডিবিএমএসের পার্থক্য সম্পর্কে নির্দিষ্ট তথ্য রয়েছে?

উত্তর:


32

আপনি বেশিরভাগই নিজের প্রশ্নের উত্তর ইতিমধ্যে দিয়েছেন yourself আমার যোগ করার জন্য কয়েকটি মুরসেল রয়েছে:

ইন পোস্টগ্রি (এবং অন্যান্য RDBMS সমর্থন booleanপ্রকার) আপনি ব্যবহার করতে পারেন booleanসরাসরি পরীক্ষার ফলাফল। এটি কাস্ট integerএবং SUM():

SUM((amount > 100)::int))

অথবা এটি একটি NULLIF()অভিব্যক্তি হিসাবে ব্যবহার করুন এবং COUNT():

COUNT(NULLIF(amount > 100, FALSE))

বা একটি সরল সহ OR NULL:

COUNT(amount > 100 OR NULL)

বা অন্যান্য বিভিন্ন এক্সপ্রেশন। অভিনয় প্রায় অভিন্নCOUNT()সাধারণত তুলনায় খুব সামান্য দ্রুত SUM()। ভিন্ন SUM()মত পল ইতিমধ্যে মন্তব্য , COUNT()কখনো আয় NULL, যা সুবিধাজনক হতে পারে। সম্পর্কিত:

যেহেতু Postgres 9.4 এর রয়েছে FILTERদফা । বিবরণ:

এটি উপরের সমস্তগুলির চেয়ে প্রায় 5 - 10% দ্বারা দ্রুত :

COUNT(*) FILTER (WHERE amount > 100)

যদি কোয়েরি আপনার পরীক্ষার ক্ষেত্রে হিসাবে সহজ হিসাবে, শুধুমাত্র একটি একক সংখ্যা ও অন্য কিছুই সঙ্গে, আপনি পুনর্লিখন করতে পারেন:

SELECT count(*) FROM tbl WHERE amount > 100;

যা সূচকের বাইরেও পারফরম্যান্সের আসল রাজা।
একটি প্রযোজ্য সূচক সহ এটি আকারের অর্ডার দ্বারা দ্রুততর হতে পারে, বিশেষত কেবলমাত্র সূচি-শুধুমাত্র স্ক্যানগুলির সাথে।

benchmarks

পোস্টগ্রিজ 10

আমি পোস্টগ্রিস 10-এর জন্য নতুন ধারাবাহিক পরীক্ষা চালিয়েছি, সমষ্টিগত FILTERধারা এবং ছোট এবং বড় সংখ্যাগুলির জন্য একটি সূচকের ভূমিকা প্রদর্শন সহ ।

সাধারণ সেটআপ:

CREATE TABLE tbl (
   tbl_id int
 , amount int NOT NULL
);

INSERT INTO tbl
SELECT g, (random() * 150)::int
FROM   generate_series (1, 1000000) g;

-- only relevant for the last test
CREATE INDEX ON tbl (amount);

ব্যাকগ্রাউন্ডের শব্দ এবং টেস্ট বিছানার নির্দিষ্টকরণের কারণে আসল সময়গুলি বেশ খানিকটা পরিবর্তিত হয়। একটি বড় পরীক্ষার সেট থেকে আদর্শ সেরা সময় দেখানো । এই দুটি ক্ষেত্রে সারমর্ম ধরা উচিত:

পরীক্ষায় 1 গণনা করে all সমস্ত সারির 1%

SELECT COUNT(NULLIF(amount > 148, FALSE))            FROM tbl; -- 140 ms
SELECT SUM((amount > 148)::int)                      FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount > 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount > 148 THEN 1 END)      FROM tbl; -- 130 ms
SELECT COUNT((amount > 148) OR NULL)                 FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount > 148)          FROM tbl; -- 118 ms -- !

SELECT count(*) FROM tbl WHERE amount > 148; -- without index  --  75 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 148; -- with index     --   1.4 ms -- !!!

ডিবি <> ফিডল এখানে

পরীক্ষা 2 গণনা ~ সমস্ত সারির 33%

SELECT COUNT(NULLIF(amount > 100, FALSE))            FROM tbl; -- 140 ms
SELECT SUM((amount > 100)::int)                      FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount > 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount > 100 THEN 1 END)      FROM tbl; -- 138 ms
SELECT COUNT(amount > 100 OR NULL)                   FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount > 100)          FROM tbl; -- 132 ms -- !

SELECT count(*) FROM tbl WHERE amount > 100; -- without index  -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 100; -- with index     --  55 ms -- !!!

ডিবি <> ফিডল এখানে

প্রতিটি সেটের সর্বশেষ পরীক্ষায় কেবলমাত্র একটি সূচক- স্ক্যান ব্যবহার করা হত , এ কারণেই এটি সমস্ত সারিগুলির এক তৃতীয়াংশ গণনা করতে সহায়তা করেছিল। সাধারণ সারিগুলির প্রায় 5% বা তার বেশি জড়িত থাকাকালীন সাদামাটা সূচক বা বিটম্যাপ সূচক স্ক্যানগুলি ক্রমিক স্ক্যানের সাথে প্রতিযোগিতা করতে পারে না।

পোস্টগ্রিস 9.1 এর জন্য পুরানো পরীক্ষা

যাচাই করতে আমি EXPLAIN ANALYZEপোস্টগ্রিজ এসকিউএল 9.1.6 এ একটি বাস্তব জীবনের টেবিলের সাথে একটি দ্রুত পরীক্ষা চালিয়েছি।

184568 এর 74208 সারি শর্তের সাথে যোগ্যতা অর্জন করেছে kat_id > 50। সমস্ত প্রশ্নের একই ফলাফল ফেরত। আমি প্রতিটি হিসাবে 10 বার ছুটে এসেছি ক্যাচিং প্রভাবগুলি বাদ দিতে এবং নোট হিসাবে সেরা ফলাফল সংযোজন:

SELECT SUM((kat_id > 50)::int)                      FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id > 50, FALSE))            FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id > 50 THEN 1 END)      FROM log_kat; -- 437 ms
SELECT COUNT((kat_id > 50) OR NULL)                 FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id > 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms

পারফরম্যান্সে খুব সম্ভবত কোনও বাস্তব পার্থক্য।


1
ফিল্টার দ্রবণটি "ধীর" গোষ্ঠী থেকে কোনও প্রকারভেদকে পরাজিত করে?
অ্যান্ড্রি এম

@ অ্যান্ড্রিএম: আমি FILTERউপরের মত প্রকাশের তুলনায় সামগ্রিকের জন্য কিছুটা দ্রুতগতিতে দেখছি (পৃষ্ঠা 9.5 দিয়ে পরীক্ষা করা)। আপনি কি একই পেতে পারি? ( WHEREএখনও পারফরম্যান্সের রাজা - যেখানে সম্ভব)।
এরউইন ব্র্যান্ডস্টেটার

কোনও পিজি হাতে পাওয়া যায় নি, তাই বলতে পারি না। যাইহোক, আমি কেবল আশা করছিলাম যে আপনি নিজের সমাধানটি সম্পূর্ণ সমাধানের জন্য শেষ সমাধানের সময় সংক্রান্ত পরিসংখ্যানগুলি দিয়ে আপডেট করবেন :)
অ্যান্ড্রি এম

@ অ্যান্ড্রিএম: অবশেষে নতুন বেঞ্চমার্ক যুক্ত করার চেষ্টা করলাম। FILTERসমাধান হল সাধারণত দ্রুত আমার পরীক্ষা।
এরউইন ব্র্যান্ডস্টেটার

11

এটি এসকিউএল সার্ভার 2012 আরটিএম-এ আমার পরীক্ষা।

if object_id('tempdb..#temp1') is not null drop table #temp1;
if object_id('tempdb..#timer') is not null drop table #timer;
if object_id('tempdb..#bigtimer') is not null drop table #bigtimer;
GO

select a.*
into #temp1
from master..spt_values a
join master..spt_values b on b.type='p' and b.number < 1000;

alter table #temp1 add id int identity(10,20) primary key clustered;

create table #timer (
    id int identity primary key,
    which bit not null,
    started datetime2 not null,
    completed datetime2 not null,
);
create table #bigtimer (
    id int identity primary key,
    which bit not null,
    started datetime2 not null,
    completed datetime2 not null,
);
GO

--set ansi_warnings on;
set nocount on;
dbcc dropcleanbuffers with NO_INFOMSGS;
dbcc freeproccache with NO_INFOMSGS;
declare @bigstart datetime2;
declare @start datetime2, @dump bigint, @counter int;

set @bigstart = sysdatetime();
set @counter = 1;
while @counter <= 100
begin
    set @start = sysdatetime();
    select @dump = count(case when number < 100 then 1 end) from #temp1;
    insert #timer values (0, @start, sysdatetime());
    set @counter += 1;
end;
insert #bigtimer values (0, @bigstart, sysdatetime());
set nocount off;
GO

set nocount on;
dbcc dropcleanbuffers with NO_INFOMSGS;
dbcc freeproccache with NO_INFOMSGS;
declare @bigstart datetime2;
declare @start datetime2, @dump bigint, @counter int;

set @bigstart = sysdatetime();
set @counter = 1;
while @counter <= 100
begin
    set @start = sysdatetime();
    select @dump = SUM(case when number < 100 then 1 else 0 end) from #temp1;
    insert #timer values (1, @start, sysdatetime());
    set @counter += 1;
end;
insert #bigtimer values (1, @bigstart, sysdatetime());
set nocount off;
GO

পৃথকভাবে রান এবং ব্যাচগুলির দিকে তাকিয়ে

select which, min(datediff(mcs, started, completed)), max(datediff(mcs, started, completed)),
            avg(datediff(mcs, started, completed))
from #timer group by which
select which, min(datediff(mcs, started, completed)), max(datediff(mcs, started, completed)),
            avg(datediff(mcs, started, completed))
from #bigtimer group by which

5 বার চালানোর পরে ফলাফলগুলি (এবং পুনরাবৃত্তি করা) বেশ অনির্বাচিত।

which                                       ** Individual
----- ----------- ----------- -----------
0     93600       187201      103927
1     93600       187201      103864

which                                       ** Batch
----- ----------- ----------- -----------
0     10108817    10545619    10398978
1     10327219    10498818    10386498

এটি দেখায় যে এসকিউএল সার্ভার টাইমারের গ্রানুলারিটি দিয়ে যখন পরিমাপ করা হয় তখন বাস্তবায়নের মধ্যে পার্থক্যের চেয়ে চলমান অবস্থার মধ্যে অনেক বেশি পরিবর্তনশীলতা রয়েছে। উভয় সংস্করণ শীর্ষে আসতে পারে এবং আমার সর্বাধিক বৈকল্পিকতা পাওয়া গেছে 2.5%।

তবে, আলাদা পদ্ধতি গ্রহণ করা:

set showplan_text on;
GO
select SUM(case when number < 100 then 1 else 0 end) from #temp1;
select count(case when number < 100 then 1 end) from #temp1;

স্টেমটেক্সট (এসইউএম)

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1011]=(0) THEN NULL ELSE [Expr1012] END))
       |--Stream Aggregate(DEFINE:([Expr1011]=Count(*), [Expr1012]=SUM([Expr1004])))
            |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [tempdb].[dbo].[#temp1].[number]<(100) THEN (1) ELSE (0) END))
                 |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#temp1]))

স্টেমটেক্সট (COUNT)

  |--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1008],0)))
       |--Stream Aggregate(DEFINE:([Expr1008]=COUNT([Expr1004])))
            |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [tempdb].[dbo].[#temp1].[number]<(100) THEN (1) ELSE NULL END))
                 |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[#temp1]))

আমার পড়া থেকে, এটি প্রদর্শিত হবে যে এসইউএম সংস্করণটি আরও কিছু করে। এটি একটি SUM ছাড়াও একটি COUNT সম্পাদন করছে । এটি বলার অপেক্ষা রাখে না যে COUNT(*)এটি আলাদা এবং এটি দ্রুত হওয়া উচিত COUNT([Expr1004])(NUL গুলি এড়িয়ে চলুন, আরও যুক্তি দিয়ে)। একজন যুক্তিবাদী অপটিমাইজার উপলব্ধি করবে [Expr1004]মধ্যে SUM([Expr1004])মধ্যে Sum সংস্করণ একটি "int- এ" টাইপ এবং তাই একটি পূর্ণসংখ্যা রেজিস্টার ব্যবহার করা হয়।

COUNTযাইহোক, আমি এখনও বিশ্বাস করি যে বেশিরভাগ আরডিবিএমএসে সংস্করণটি আরও দ্রুততর হবে, তবে পরীক্ষার থেকে আমার সিদ্ধান্তটি এই যে আমি SUM(.. 1.. 0..)ভবিষ্যতে সাথে যাব , কমপক্ষে এসকিউএল সার্ভারের জন্য এএনএসআই সতর্কতাগুলি ব্যবহার করার সময় উত্থাপিত হওয়া ব্যতীত অন্য কোনও কারণে নয় for COUNT


1

আমার অভিজ্ঞতার মধ্যে প্রায় 10,000,000 এর অনুসন্ধানের ক্ষেত্রে দুটি পদ্ধতির জন্য আমি লক্ষ্য করেছি যে গণনা (*) প্রায় দুইবার সিপিইউ ব্যবহার করে এবং আরও দ্রুত চালায়। তবে আমার প্রশ্নগুলি ফিল্টার ছাড়াই।

COUNT (*)

CPU...........: 1828   
Execution time:  470 ms  

যোগফল (1)

CPU...........: 3859  
Execution time:  681 ms  

আপনি এই পরীক্ষার জন্য কোন আরডিবিএমএস ব্যবহার করেছেন তা নির্দিষ্ট করে দেওয়া উচিত।
ইমেজ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.