views:

866

answers:

2

I have 3 tables :

TABLE MyColumn (
  ColumnId INT NOT NULL,
  Label VARCHAR(80) NOT NULL,
  PRIMARY KEY (ColumnId)
)

TABLE MyPeriod (
  PeriodId CHAR(6) NOT NULL, -- format yyyyMM
  Label VARCHAR(80) NOT NULL,
  PRIMARY KEY (PeriodId)
)

TABLE MyValue (
  ColumnId INT NOT NULL,
  PeriodId CHAR(6) NOT NULL,
  Amount DECIMAL(8, 4) NOT NULL,
  PRIMARY KEY (ColumnId, PeriodId),
  FOREIGN KEY (ColumnId) REFERENCES MyColumn(ColumnId),
  FOREIGN KEY (PeriodId) REFERENCES MyPeriod(PeriodId)
)

MyValue's rows are only created when a real value is provided.

I want my results in a tabular way, as :

Column   | Month 1 | Month 2 | Month 4 | Month 5 |
Potatoes |   25.00 |    5.00 |    1.60 |    NULL |
Apples   |    2.00 |    1.50 |    NULL |    NULL |

I have successfully created a cross-join :

SELECT
    MyColumn.Label AS [Column],
    MyPeriod.Label AS [Period],
    ISNULL(MyValue.Amount, 0) AS [Value]
FROM
    MyColumn CROSS JOIN
    MyPeriod LEFT OUTER JOIN
    MyValue ON (MyValue.ColumnId = MyColumn.ColumnId AND MyValue.PeriodId = MyPeriod.PeriodId)

Or, in linq :

from p in MyPeriods
from c in MyColumns
join v in MyValues on new { c.ColumnId, p.PeriodId } equals new { v.ColumnId, v.PeriodId } into values
from nv in values.DefaultIfEmpty()
select new {
    Column = c.Label,
    Period = p.Label,
    Value = nv.Amount
}

And seen how to create a pivot in linq (here or here) :

(assuming MyDatas is a view with the result of the previous query) :

from c in MyDatas
group c by c.Column into line
select new
{
  Column = line.Key,
  Month1 = line.Where(l => l.Period == "Month 1").Sum(l => l.Value),
  Month2 = line.Where(l => l.Period == "Month 2").Sum(l => l.Value),
  Month3 = line.Where(l => l.Period == "Month 3").Sum(l => l.Value),
  Month4 = line.Where(l => l.Period == "Month 4").Sum(l => l.Value)
}

But I want to find a way to create a resultset with, if possible, Month1, ... properties dynamic.

Note : A solution which results in a n+1 query :

from c in MyDatas
group c by c.Column into line
select new
{
    Column = line.Key,
    Months =
     from l in line
     group l by l.Period into period
     select new
     {
      Period = period.Key,
      Amount = period.Sum(l => l.Value)
     }
}
A: 

Do you have calendar periods (1-12) or fiscal periods (1-12 or 1-13 depending on the year)?

Would you only run the report for one year?

If possible, I would break the PeriodId into two fields of Year & Period. This will make it easier to select for a specific year. The queries will be simpler.

Paul Morgan
+1  A: 

Check out the accepted answer to this question:

Dynamic SQL to generate column names?

magnifico
I can work, yes
Styx31