2

linq-to-entitiesを介してアクセスする次のテーブルがあります。

  • タイプ(1または2)

同じ日に同じタイプの行がたくさんあるので、月ごとにそれらの金額を要約する必要があります。簡単だ:

from r in rows
group r by new { r.Date.Year, r.Date.Month }
into g
select
    new
        {
            Date = new DateTime(g.Key.Year, g.Key.Month, 1),
            Hours = g.Sum(a => a.Amount)
        };

ただし、同じLINQに実装する必要のある特別なルールがあり、次の点でサポートが必要です。

特定の日にタイプ2がある場合、その日はタイプ2の合計のみである必要があります。それ以外の場合は、タイプ1に要約する必要があります。

タイプ1とタイプ2の違いは1日あたりであり、合計は1か月あたりであることに注意してください。

更新 大量のデータを処理しているため、1回のデータベース呼び出しですべてを取得する必要があります。メモリにロードしてそこで管理することはできません。

4

4 に答える 4

1

Okay, I think I've got this figured out. I've tested it with some data, and that works.

Testdata and SQL testquery:

DECLARE @table TABLE (
    Datum DATETIME,
    Amount INT,
    [Type] INT
)

INSERT INTO @table (Datum, Amount, [Type]) values
('2012-01-01',200,1),
('2012-01-01',100,2),
('2012-01-02',500,1),
('2012-03-01',200,1),
('2012-03-01',100,1),
('2012-03-02',500,2)

SELECT MONTH(Datum), YEAR(Datum), COUNT(*), SUM(Amount)
FROM @table t
INNER JOIN (
    SELECT DAY (Datum) AS _day, MONTH(Datum) AS _month, YEAR(Datum) _year,
    MAX([Type]) as _type
    FROM @table
    GROUP BY DAY (Datum), MONTH(Datum), YEAR(Datum)
) X
ON _month = MONTH (T.Datum)
AND _year = YEAR(T.Datum)
AND _day = DAY(T.Datum)
AND _type = T.[Type]
GROUP BY MONTH(Datum), YEAR(Datum)

Result:

(No column name)    (No column name)    (No column name)    (No column name)
1   2012    2   600
3   2012    3   800

The translated LINQ query, tested with L2S and a 'real' test-table with test-data.

using (DataClasses1DataContext ctx = new DataClasses1DataContext()) {
    var rows = ctx.Tests;

    var query = rows
        .Join(
            rows.GroupBy(rr =>
                new { rr.Datum.Day, rr.Datum.Month, rr.Datum.Year },
                (key, data) => new { Year = key.Year, Month = key.Month, Day = key.Day, MaxType = data.Select(xxx => xxx.Type).Max() }
            ),
            rr => new { Day = rr.Datum.Day, Month = rr.Datum.Month, Year = rr.Datum.Year, Type = rr.Type },
            rr => new { Day = rr.Day, Month = rr.Month, Year = rr.Year, Type = rr.MaxType },
            (r, r1) => r

        )
        .GroupBy(r =>
            new { Year = r.Datum.Year, Month = r.Datum.Month },
            (key, data) => new { Year = key.Year, Month = key.Month, Amount = data.Select(xx => xx.Amount).Sum() }
        )
        .ToList();
}

This returns the same results.

And, for the fun of it, the SQL-query that L2S generated from the Linq query.

SELECT [t7].[value] AS [Year], [t7].[value2] AS [Month], (
    SELECT SUM([t8].[Amount])
    FROM [dbo].[Test] AS [t8]
    INNER JOIN (
        SELECT [t11].[value3], [t11].[value2], [t11].[value], (
            SELECT MAX([t12].[Type])
            FROM [dbo].[Test] AS [t12]
            WHERE ((([t11].[value] IS NULL) AND (DATEPART(Day, [t12].[Datum]) IS NULL)) OR (([t11].[value] IS NOT NULL) AND (DATEPART(Day, [t12].[Datum]) IS NOT NULL) AND ((([t11].[value] IS NULL) AND (DATEPART(Day, [t12].[Datum]) IS NULL)) OR (([t11].[value] IS NOT NULL) AND (DATEPART(Day, [t12].[Datum]) IS NOT NULL) AND ([t11].[value] = DATEPART(Day, [t12].[Datum])))))) AND ((([t11].[value2] IS NULL) AND (DATEPART(Month, [t12].[Datum]) IS NULL)) OR (([t11].[value2] IS NOT NULL) AND (DATEPART(Month, [t12].[Datum]) IS NOT NULL) AND ((([t11].[value2] IS NULL) AND (DATEPART(Month, [t12].[Datum]) IS NULL)) OR (([t11].[value2] IS NOT NULL) AND (DATEPART(Month, [t12].[Datum]) IS NOT NULL) AND ([t11].[value2] = DATEPART(Month, [t12].[Datum])))))) AND ((([t11].[value3] IS NULL) AND (DATEPART(Year, [t12].[Datum]) IS NULL)) OR (([t11].[value3] IS NOT NULL) AND (DATEPART(Year, [t12].[Datum]) IS NOT NULL) AND ((([t11].[value3] IS NULL) AND (DATEPART(Year, [t12].[Datum]) IS NULL)) OR (([t11].[value3] IS NOT NULL) AND (DATEPART(Year, [t12].[Datum]) IS NOT NULL) AND ([t11].[value3] = DATEPART(Year, [t12].[Datum]))))))
            ) AS [value4]
        FROM (
            SELECT [t10].[value], [t10].[value2], [t10].[value3]
            FROM (
                SELECT DATEPART(Day, [t9].[Datum]) AS [value], DATEPART(Month, [t9].[Datum]) AS [value2], DATEPART(Year, [t9].[Datum]) AS [value3]
                FROM [dbo].[Test] AS [t9]
                ) AS [t10]
            GROUP BY [t10].[value], [t10].[value2], [t10].[value3]
            ) AS [t11]
        ) AS [t13] ON (DATEPART(Day, [t8].[Datum]) = [t13].[value]) AND (DATEPART(Month, [t8].[Datum]) = [t13].[value2]) AND (DATEPART(Year, [t8].[Datum]) = [t13].[value3]) AND ([t8].[Type] = [t13].[value4])
    WHERE ((([t7].[value] IS NULL) AND (DATEPART(Year, [t8].[Datum]) IS NULL)) OR (([t7].[value] IS NOT NULL) AND (DATEPART(Year, [t8].[Datum]) IS NOT NULL) AND ((([t7].[value] IS NULL) AND (DATEPART(Year, [t8].[Datum]) IS NULL)) OR (([t7].[value] IS NOT NULL) AND (DATEPART(Year, [t8].[Datum]) IS NOT NULL) AND ([t7].[value] = DATEPART(Year, [t8].[Datum])))))) AND ((([t7].[value2] IS NULL) AND (DATEPART(Month, [t8].[Datum]) IS NULL)) OR (([t7].[value2] IS NOT NULL) AND (DATEPART(Month, [t8].[Datum]) IS NOT NULL) AND ((([t7].[value2] IS NULL) AND (DATEPART(Month, [t8].[Datum]) IS NULL)) OR (([t7].[value2] IS NOT NULL) AND (DATEPART(Month, [t8].[Datum]) IS NOT NULL) AND ([t7].[value2] = DATEPART(Month, [t8].[Datum]))))))
    ) AS [Amount]
FROM (
    SELECT [t6].[value], [t6].[value2]
    FROM (
        SELECT DATEPART(Year, [t0].[Datum]) AS [value], DATEPART(Month, [t0].[Datum]) AS [value2]
        FROM [dbo].[Test] AS [t0]
        INNER JOIN (
            SELECT [t3].[value3], [t3].[value2], [t3].[value], (
                SELECT MAX([t4].[Type])
                FROM [dbo].[Test] AS [t4]
                WHERE ((([t3].[value] IS NULL) AND (DATEPART(Day, [t4].[Datum]) IS NULL)) OR (([t3].[value] IS NOT NULL) AND (DATEPART(Day, [t4].[Datum]) IS NOT NULL) AND ((([t3].[value] IS NULL) AND (DATEPART(Day, [t4].[Datum]) IS NULL)) OR (([t3].[value] IS NOT NULL) AND (DATEPART(Day, [t4].[Datum]) IS NOT NULL) AND ([t3].[value] = DATEPART(Day, [t4].[Datum])))))) AND ((([t3].[value2] IS NULL) AND (DATEPART(Month, [t4].[Datum]) IS NULL)) OR (([t3].[value2] IS NOT NULL) AND (DATEPART(Month, [t4].[Datum]) IS NOT NULL) AND ((([t3].[value2] IS NULL) AND (DATEPART(Month, [t4].[Datum]) IS NULL)) OR (([t3].[value2] IS NOT NULL) AND (DATEPART(Month, [t4].[Datum]) IS NOT NULL) AND ([t3].[value2] = DATEPART(Month, [t4].[Datum])))))) AND ((([t3].[value3] IS NULL) AND (DATEPART(Year, [t4].[Datum]) IS NULL)) OR (([t3].[value3] IS NOT NULL) AND (DATEPART(Year, [t4].[Datum]) IS NOT NULL) AND ((([t3].[value3] IS NULL) AND (DATEPART(Year, [t4].[Datum]) IS NULL)) OR (([t3].[value3] IS NOT NULL) AND (DATEPART(Year, [t4].[Datum]) IS NOT NULL) AND ([t3].[value3] = DATEPART(Year, [t4].[Datum]))))))
                ) AS [value4]
            FROM (
                SELECT [t2].[value], [t2].[value2], [t2].[value3]
                FROM (
                    SELECT DATEPART(Day, [t1].[Datum]) AS [value], DATEPART(Month, [t1].[Datum]) AS [value2], DATEPART(Year, [t1].[Datum]) AS [value3]
                    FROM [dbo].[Test] AS [t1]
                    ) AS [t2]
                GROUP BY [t2].[value], [t2].[value2], [t2].[value3]
                ) AS [t3]
            ) AS [t5] ON (DATEPART(Day, [t0].[Datum]) = [t5].[value]) AND (DATEPART(Month, [t0].[Datum]) = [t5].[value2]) AND (DATEPART(Year, [t0].[Datum]) = [t5].[value3]) AND ([t0].[Type] = [t5].[value4])
        ) AS [t6]
    GROUP BY [t6].[value], [t6].[value2]
    ) AS [t7]

I have no idea how efficient this SQL is, you're going to have to try it.

于 2012-07-31T12:59:11.743 に答える
1

これはあなたにどのように見えますか?

var query =
    from r in rows.ToArray()
    group r by new { r.Date.Year, r.Date.Month } into g
    let lookup = g.ToLookup(x => x.Type, x => x.Amount)
    let Hours = lookup[2].Any() ? lookup[2].Sum() : lookup[1].Sum()
    select new
    {
        Date = new DateTime(g.Key.Year, g.Key.Month, 1),
        Hours,
    };

.ToArray()これを機能させるには、データをメモリに取り込む必要があることに注意してください。

これは、またはのいずれかTypeの値を持つ整数であると想定しました。12


さらに考えてみると、この種のグループ化を1つのクエリでメモリに入れずに行うことはできないと思います。

したがって、最善の代替策は、メモリを最小限に抑えることです。これが機能しない場合は、いくつかのクエリに分割する必要があります。

var query1 =
    from r in rows
    group r.Amount by new
    {
        r.Date.Year,
        r.Date.Month,
        r.Type,
    } into g
    select new
    {
        g.Key.Year,
        g.Key.Month,
        g.Key.Type,
        Amount = g.Sum(),
    };

var query2 =
    from r in query1.ToArray()
    group r by new
    {
        r.Year,
        r.Month,
    } into g
    let lookup = g.ToLookup(x => x.Type, x => x.Amount)
    let Hours = lookup[2].Any() ? lookup[2].Sum() : lookup[1].Sum()
    select new
    {
        Date = new DateTime(g.Key.Year, g.Key.Month, 1),
        Hours,
    };

これはオプションですか?

于 2012-07-31T11:16:23.933 に答える
1
rows
    .GroupBy(
        r => new { r.Date.Year, r.Date.Month, r.Date.Day, r.Type },
        (r, rr) => new { r.Year, r.Month, r.Day, r.Type, Amount = rr.Sum(rrr => rrr.Amount) })
    .GroupBy(
        r => new { r.Year, r.Month, r.Day },
        (r, rr) => new { r.Year, r.Month, r.Day, Amount = rr.OrderByDescending(rrr => rrr.Type).Select(rrr => rrr.Amount).First() })
    .GroupBy(
        r => new { r.Year, r.Month },
        (r, rr) => new { r.Year, r.Month, Amount = rr.Sum(rrr => rrr.Amount) })

この背後にある比率は非常に単純です。「タイプ2レコードが少なくとも1つある場合、タイプ2レコードのみを合計する」という要件は、レコードをタイプごとにグループ化するだけで実装できます(もちろん数日以内)。なぜそれが機能するのですか?すべてのレコードを2つのグループに分割するため、タイプ2(タイプ2レコードが少なくとも1つある場合に使用する必要があります)とタイプ1(実際には「タイプ2が存在しない場合のすべてのレコード」を意味します)。2番目の部分(合計の選択)はさらに単純です。グループを(1日以内に)降順のタイプ(つまり、タイプ2の合計、タイプ1の合計)で並べ替え、最初の部分を取得します。これにより、存在する場合はタイプ2、そうでない場合はタイプ1になります。

率直に言って、それは誰もが嫌う一種の「スマートコード」です。なぜなら、それがどのように機能するかを一目で理解できる人は誰もいないからです。

于 2012-07-31T13:11:14.373 に答える
0

これはどう?

from r in rows
group r by new { r.Date.Year, r.Date.Month }
into g
let type2Days = g.Where( a => a.Type == 2 ).Select( a => a.Date.Day ).Distinct()
let filtered = g.Where( a => a.Type == 2 || type2Days.Contains(a.Date.Day) == false )
select
    new
        {
            Date = new DateTime(g.Key.Year, g.Key.Month, 1),
            Hours = filtered.Sum(a => a.Amount)
        };
于 2012-07-31T23:00:51.127 に答える