বিষয়বস্তু লোড না করে সত্তা ফ্রেমওয়ার্কের মধ্যে কীভাবে COUNT সারি করবেন?


109

আমি সারণী ফ্রেমওয়ার্কটি ব্যবহার করে কোনও টেবিলে কীভাবে ম্যাচের সারিগুলি গণনা করব তা নির্ধারণ করার চেষ্টা করছি ।

সমস্যাটি হ'ল প্রতিটি সারিতে অনেক মেগাবাইট ডেটা থাকতে পারে (একটি বাইনারি ক্ষেত্রে)। অবশ্যই এসকিউএল এর কিছু হবে:

SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1';

আমি সমস্ত সারি লোড করতে পারতাম এবং এর সাথে গণনাটি খুঁজে পেতে পারি:

var owner = context.MyContainer.Where(t => t.ID == '1');
owner.MyTable.Load();
var count = owner.MyTable.Count();

তবে তা চূড়ান্তভাবে অক্ষম। একটি সহজ উপায় আছে কি?


সম্পাদনা: ধন্যবাদ, সব। আমি একটি প্রাইভেট সংযুক্ত থেকে ডিবি স্থানান্তরিত করেছি যাতে আমি প্রোফাইল চালাতে পারি; এটি সাহায্য করে কিন্তু বিভ্রান্তির সৃষ্টি করে যা আমি প্রত্যাশা করি না।

এবং আমার আসল তথ্যটি আরও গভীরতর, আমি আইটেমস এর কেসস অফ প্যালেট বহনকারী ট্রাকগুলি ব্যবহার করব - এবং আমি কমপক্ষে একটি আইটেম না থাকলে ট্রাকটি ছেড়ে যেতে চাই না ।

আমার প্রচেষ্টা নীচে প্রদর্শিত হয়। আমি যে অংশটি পাই না তা হ'ল CASE_2 কখনই ডিবি সার্ভার (এমএসএসকিউএল) অ্যাক্সেস করে না।

var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID));
if (truck == null)
    return "Invalid Truck ID: " + truckID;
var dlist = from t in ve.Truck
    where t.ID == truckID
    select t.Driver;
if (dlist.Count() == 0)
    return "No Driver for this Truck";

var plist = from t in ve.Truck where t.ID == truckID
    from r in t.Pallet select r;
if (plist.Count() == 0)
    return "No Pallets are in this Truck";
#if CASE_1
/// This works fine (using 'plist'):
var list1 = from r in plist
    from c in r.Case
    from i in c.Item
    select i;
if (list1.Count() == 0)
    return "No Items are in the Truck";
#endif

#if CASE_2
/// This never executes any SQL on the server.
var list2 = from r in truck.Pallet
        from c in r.Case
        from i in c.Item
        select i;
bool ok = (list.Count() > 0);
if (!ok)
    return "No Items are in the Truck";
#endif

#if CASE_3
/// Forced loading also works, as stated in the OP...
bool ok = false;
foreach (var pallet in truck.Pallet) {
    pallet.Case.Load();
    foreach (var kase in pallet.Case) {
        kase.Item.Load();
        var item = kase.Item.FirstOrDefault();
        if (item != null) {
            ok = true;
            break;
        }
    }
    if (ok) break;
}
if (!ok)
    return "No Items are in the Truck";
#endif

এবং এসকিউএল CASE_1 ফলে মাধ্যমে পাইপ হয় sp_executesql কিন্তু:

SELECT [Project1].[C1] AS [C1]
FROM   ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN  (SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM   [dbo].[PalletTruckMap] AS [Extent1]
        INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID]
        INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID]
        WHERE [Extent1].[TruckID] = '....'
    )  AS [GroupBy1] ) AS [Project1] ON 1 = 1

[ আমার কাছে আসলে ট্রাক, ড্রাইভার, প্যালেটস, কেস বা আইটেম নেই; এসকিউএল থেকে আপনি দেখতে পাচ্ছেন যে ট্রাক-প্যালেট এবং প্যালেট-কেস সম্পর্কগুলি অনেকগুলি-অনেকগুলি - যদিও আমি মনে করি না যে এটি গুরুত্বপূর্ণ। আমার আসল অবজেক্টগুলি অদৃশ্য এবং বর্ণনা করা আরও শক্ত, সুতরাং আমি নাম পরিবর্তন করেছি। ]


1
প্যালেট লোডিং সমস্যাটি কীভাবে সমাধান করলেন?
শার্লক

উত্তর:


123

অনুসন্ধান বাক্য গঠন:

var count = (from o in context.MyContainer
             where o.ID == '1'
             from t in o.MyTable
             select t).Count();

পদ্ধতি বাক্য গঠন:

var count = context.MyContainer
            .Where(o => o.ID == '1')
            .SelectMany(o => o.MyTable)
            .Count()

উভয়ই একই এসকিউএল কোয়েরি তৈরি করে।


কেন SelectMany()? এটা কি দরকার? এটি ছাড়া এটি সঠিকভাবে কাজ করবে না?
জো স্মু

@ জোস্মো, না, এটি সম্পূর্ণ আলাদা কোয়েরি।
ক্রেগ স্টান্টজ

আমার জন্য এটি সাফ করার জন্য আপনাকে ধন্যবাদ। শুধু নিশ্চিত হতে চেয়েছিলেন। :)
জো স্মু

1
আপনি কি আমাকে বলতে পারবেন কেন সিলেক্টম্যানির সাথে এটি আলাদা? আমি বুঝতে পারছি না। আমি সিলেকম্যানি ছাড়া এটি করি তবে এটি সত্যই ধীর হয়ে যায় কারণ আমার কাছে 20 মিলিয়ন রেকর্ড রয়েছে। আমি ইয়াং জাং থেকে উত্তরটি চেষ্টা করেছিলাম এবং দুর্দান্ত কাজ করেছিলাম, কেবল নির্বাচিত মানুষ কী তা জানতে চেয়েছিল।
মাইকসফট

1
@ অ্যাসটিনফেলাইপ সিলেক্টম্যানিকে কল না করে, ক্যোয়ারী মাইকন্টেইনারটিতে সারণীর সংখ্যা '1' এর সমান আইডি দিয়ে ফিরিয়ে আনবে। সিলেক্টম্যানি কল মাই টেবিলের সমস্ত সারি ফেরত দেয় যা ক্যোয়ারির আগের ফলাফলের সাথে সম্পর্কিত (যার অর্থ ফলাফল MyContainer.Where(o => o.ID == '1'))
sbecker

48

আমি মনে করি আপনি কিছু চান

var count = context.MyTable.Count(t => t.MyContainer.ID == '1');

(মন্তব্যগুলি প্রতিফলিত করতে সম্পাদিত)


1
না, তার মাই টেবিলের সত্ত্বার গণনা প্রয়োজন মাই কন্টেইনার আইডি = 1 সহ এক সত্তা দ্বারা রেফারেন্স করা
ক্রেগ স্টান্টজ

3
ঘটনাচক্রে, t.ID যদি পিকে হয়, তবে উপরের কোডটিতে সর্বদা গণনা 1 হবে :) :)
ক্রেগ স্টান্টজ

2
@ ক্রেইগ, আপনি ঠিক বলেছেন, আমার t.ForeignTable.ID ব্যবহার করা উচিত ছিল। আপডেট করা হয়েছে।
কেভিন

1
ভাল এটি সংক্ষিপ্ত এবং সহজ। আমার পছন্দটি: var count = context.MyTable.Count(t => t.MyContainer.ID == '1'); দীর্ঘ এবং কুরুচিপূর্ণ নয়: var count = (from o in context.MyContainer where o.ID == '1' from t in o.MyTable select t).Count(); তবে এটি কোডিং স্টাইলের উপর নির্ভর করে ...
সিএল

নিশ্চিত করুন যে আপনি "সিস্টেম.লিনক ব্যবহার করে" অন্তর্ভুক্ত করেছেন, বা এই
অভ্যাসটি

16

আমি এটি বুঝতে পেরে, নির্বাচিত উত্তরটি এখনও সম্পর্কিত সমস্ত পরীক্ষা লোড করে। এই এমএসডিএন ব্লগ অনুসারে আরও একটি ভাল উপায় আছে।

http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx

বিশেষভাবে

using (var context = new UnicornsContext())

    var princess = context.Princesses.Find(1);

    // Count how many unicorns the princess owns 
    var unicornHaul = context.Entry(princess)
                      .Collection(p => p.Unicorns)
                      .Query()
                      .Count();
}

3
অতিরিক্ত Find(1)অনুরোধ করার দরকার নেই । কেবল সত্তাটি তৈরি করুন এবং প্রসঙ্গে যুক্ত করুন:var princess = new PrincessEntity{ Id = 1 }; context.Princesses.Attach(princess);
টেবিলগুলি

13

এটি আমার কোড:

IQueryable<AuctionRecord> records = db.AuctionRecord;
var count = records.Count();

ভেরিয়েবলটি আইকুয়েরেবল হিসাবে সংজ্ঞায়িত হয়েছে তা নিশ্চিত করুন আপনি যখন গণনা () পদ্ধতি ব্যবহার করবেন তখন EF এর মতো কিছু কার্যকর করা হবে

select count(*) from ...

অন্যথায়, যদি রেকর্ডগুলি IEnumerable হিসাবে সংজ্ঞায়িত করা হয়, উত্পন্ন sql পুরো টেবিলটি জিজ্ঞাসা করবে এবং সারিগুলি গণনা করবে।


10

ভাল, এমনকি SELECT COUNT(*) FROM Table বিশেষত বৃহত টেবিলগুলিতেও এটি মোটামুটি অদক্ষ হয়ে উঠবে, যেহেতু এসকিউএল সার্ভার সত্যিই পুরো টেবিল স্ক্যান (ক্লাস্টারড ইনডেক্স স্ক্যান) ছাড়া কিছুই করতে পারে না।

কখনও কখনও, ডাটাবেস থেকে সারিগুলির একটি আনুমানিক সংখ্যা জানার পক্ষে এটি যথেষ্ট ভাল এবং এই জাতীয় ক্ষেত্রে এর মতো বিবৃতি যথেষ্ট হতে পারে:

SELECT 
    SUM(used_page_count) * 8 AS SizeKB,
    SUM(row_count) AS [RowCount], 
    OBJECT_NAME(OBJECT_ID) AS TableName
FROM 
    sys.dm_db_partition_stats
WHERE 
    OBJECT_ID = OBJECT_ID('YourTableNameHere')
    AND (index_id = 0 OR index_id = 1)
GROUP BY 
    OBJECT_ID

এটি গতিশীল পরিচালন দর্শনটি পরীক্ষা করবে এবং একটি নির্দিষ্ট সারণী প্রদত্ত সারিগুলির সংখ্যা এবং এটি থেকে টেবিলের আকার বের করবে। এটি হিপ (index_id = 0) বা ক্লাস্টারড ইনডেক্স (সূচি_আড = 1) এর জন্য সংখ্যার যোগফল যোগ করে এটি করে।

এটি দ্রুত, এটি ব্যবহার করা সহজ, তবে এটি 100% সঠিক বা আপ টু ডেট হওয়ার গ্যারান্টিযুক্ত নয়। তবে অনেক ক্ষেত্রে এটি "যথেষ্ট ভাল" (এবং সার্ভারে খুব কম বোঝা চাপিয়ে দেয়)।

সম্ভবত এটি আপনার পক্ষেও কাজ করবে? অবশ্যই, এটি এএফ এ ব্যবহার করতে, আপনি এটি কোনও সঞ্চিত প্রকোপগুলিতে গুটিয়ে রাখতে চান বা সরাসরি "এসকিউএল এক্সিকিউট করুন" কলটি ব্যবহার করতে পারেন।

আঙ্গুরের ছিরড়া


1
WHERE এ এফ কে রেফারেন্সের কারণে এটি একটি পূর্ণ টেবিল স্ক্যান হবে না। কেবলমাত্র মাস্টারের বিশদই স্ক্যান করা হবে। তার যে পারফরম্যান্সের সমস্যা ছিল তা হ'ল ব্লব ডেটা লোড করা, রেকর্ড গণনা নয়। অনুমান করা যায় যে মাস্টার রেকর্ডে প্রতি দশক বিশিষ্ট রেকর্ডের বিশদ রেকর্ড নেই, আমি আসলে এমন কিছুকে "অনুকূলিত" করব না যা আসলে ধীর নয়।
ক্রেগ স্টান্টজ

ঠিক আছে, হ্যাঁ, সেক্ষেত্রে আপনি কেবল একটি উপসেট বেছে নেবেন - তা ঠিক হওয়া উচিত। ব্লব ডেটা হিসাবে - আমি এই ধারণাটির মধ্যে ছিলাম যে আপনি কোনও ইএফ টেবিলের যে কোনও কলামে "ডিফার্ড লোডিং" সেট করতে পারেন যাতে এটি লোড করা এড়াতে পারে, যাতে এটি সহায়তা করতে পারে।
marc_s

সত্তা ফ্রেমওয়ার্কের সাথে এই এসকিউএল ব্যবহার করার কোনও উপায় আছে কি? যাইহোক, এই ক্ষেত্রে আমি কেবল সেখানে সারি সারণী আছে তা জানতে হবে, তবে আমি ইচ্ছাকৃতভাবে প্রশ্নটি আরও সাধারণভাবে জিজ্ঞাসা করেছি।
এনভিআরএম

4

সত্তা প্রসঙ্গে এক্সিকিউটস্টোরকিউরি পদ্ধতিটি ব্যবহার করুন । এটি একটি সাধারণ সারির গণনা করার জন্য পুরো ফলাফল সেটটি ডাউনলোড করা এবং অবজেক্টগুলিতে ডিসরিয়ালাইজেশন এড়ানো যায়।

   int count;

    using (var db = new MyDatabase()){
      string sql = "SELECT COUNT(*) FROM MyTable where FkId = {0}";

      object[] myParams = {1};
      var cntQuery = db.ExecuteStoreQuery<int>(sql, myParams);

      count = cntQuery.First<int>();
    }

6
আপনি যদি লিখেন int count = context.MyTable.Count(m => m.MyContainerID == '1')তবে উত্পন্ন এসকিউএল আপনি যা করছেন ঠিক তার মতো হবে তবে কোডটি খুব সুন্দর। কোনও সত্তা স্মৃতিতে লোড হয় না। আপনি যদি চান তবে লিনকপ্যাডে চেষ্টা করে দেখুন - এটি আপনাকে কভারগুলির নীচে ব্যবহৃত এসকিউএল দেখায়।
ড্র নোকস

ইন-লাইন এসকিউএল। । আমার প্রিয় জিনিস না।
ডুয়ান্ন

3

আমি এই কাজ করা উচিত...

var query = from m in context.MyTable
            where m.MyContainerId == '1' // or what ever the foreign key name is...
            select m;

var count = query.Count();

আমি প্রথমেও এই দিকে গিয়েছিলাম এই দিকটি, তবে এটি আমার বোঝা যে আপনি নিজে নিজে এটিকে যুক্ত না করলে আমার মাই কন্টেইনার সম্পত্তি থাকবে তবে মাইকন্টেইনারআইড থাকবে না। সুতরাং, আপনি যা পরীক্ষা করতে চান তা হ'ল মি। মাইকন্টেইনার.আইডি।
কেভিন

মাইকন্টেইনার যদি পিতা-মাতা হয় এবং মাই টেবিল সম্পর্কের সন্তান হয় তবে আপনাকে কিছু বিদেশী কী দিয়ে সেই সম্পর্কটি স্থাপন করতে হয়েছিল, আমি নিশ্চিত নই যে আপনি কীভাবে জানতে পারবেন যে কোন মাইটেবল সত্তা যেখানে মাইকন্টেইনার সত্তার সাথে সম্পর্কিত ... তবে সম্ভবত আমি কাঠামো সম্পর্কে ধারণা নিয়েছে ...
বাইটেন্ডার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.