2014-02-19 3 views
0

у меня не было места, чтобы сделать название более описательным, извините, но это то, что я пытаюсь сделать:SQL - Как иметь несколько запросов в заявлении без петли или объединения всех

Я создание таблицы данных для гистограммы Google на веб-странице. Я просматриваю данные клиента и счета, и хочу получить снимок состояния клиента за каждую неделю предыдущего периода (неделя, месяц, квартал, год). Я знаю, что моя инструкция SELECT должна быть в течение недели, и я могу сделать это, используя UNION ALL, но это похоже на много кода для поддержки, если я хочу каждую неделю в течение последних трех лет. Там будут и другие фильтры, которые я снял для этого примера (MS SQL 2008):

SELECT COUNT(distinct customer.AccountNumber) 
    FROM Datahub..InvoiceHold invoice 
     INNER JOIN Datahub..CustomerBase as customer ON invoice.AccountNumber = customer.AccountNumber 
     INNER JOIN Datahub..OrderHold as orders ON invoice.AccountNumber = orders.AccountNumber 
    WHERE customer.CoNo = '003' 
     AND customer.CustomerStartDate <= GETDATE() - 7 
     AND invoice.InvoiceDate > GETDATE() - 7 
     AND orders.OrderDate > GETDATE() - 7 
    UNION ALL 
    SELECT COUNT(distinct customer.AccountNumber) 
    FROM Datahub..InvoiceHold invoice 
     INNER JOIN Datahub..CustomerBase as customer ON invoice.AccountNumber = customer.AccountNumber 
     INNER JOIN Datahub..OrderHold as orders ON invoice.AccountNumber = orders.AccountNumber 
    WHERE customer.CoNo = '003' 
     AND customer.CustomerStartDate <= GETDATE() - 14 
     AND invoice.InvoiceDate > GETDATE() - 14 
     AND orders.OrderDate > GETDATE() - 14 
    UNION ALL........And so on 

Я думал о динамически писать строку SQL в моем вызывающем коде, и я могу легко сделать цикл там и сцепить строку запроса, которую я отправляю на MS SQL 2008. Но если я хочу сделать это в хранимой процедуре, как бы я сужу это до одного утверждения? Я читал, что использование циклов в SQL - это не-нет, за исключением узкого круга ситуаций, и я не понимаю использование курсоров. Кажется, это должно быть легко, но я не парень SQL.

EDIT Я пробовал представленные решения и не получал такого качества, которого я искал, как ни странно. Это заняло 7 минут или больше. Я не знаю, почему. В любом случае, я закончил делать некоторую фильтрацию данных и вытащил ее в память, а затем использовал LINQ в dataTable и пошел оттуда. Это может быть не очень красиво, но это занимает около 20 секунд.

Большое спасибо за ответы!

+0

Почему бы не сохранить результаты запроса в таблице темпа? Вы можете создать цикл WHILE для отдельных недель и просто сохранить INSERT значения в таблице. – SchmitzIT

+0

Вы можете добавить OVER к вашему счету и сделать их «функциями окна». Тогда вам нужен только один запрос с большим количеством подсчетов, который можно построить динамически. Однако я бы, скорее всего, рассмотрел только предварительные вычисления каждую неделю и сохранил их в таблице –

ответ

0

Я пробовал предлагаемые решения и не получал такого качества, которого я искал, как ни странно. Это заняло 7 минут или больше. Я не знаю, почему. В любом случае, я закончил делать некоторую фильтрацию данных и вытащил ее в память, а затем использовал LINQ в dataTable и пошел оттуда. Это может быть не очень красиво, но это занимает около 20 секунд. Вот мой код, если кто-либо когда-либо захочет сделать что-то подобное:

public IList<object[]> GetBarChartData(string timeframe, string channel, string unit, string division) 
    { 
     DataTable dt = new DataTable(); 
     List<CustomerClass> myList = new List<CustomerClass>(); 
     List<object[]> retVal = new List<object[]>(); 
     CustomerClass myClass = new CustomerClass(); 

     string marketChannel = (channel == "ALL MARKET CHANNELS" ? string.Empty : "AND customer.MarketChannelDescription = '" + channel + "' "); 
     string businessUnit = (unit == "ALL BUSINESS UNITS" ? string.Empty : "AND customer.BusinessUnitDescription = '" + unit + "' "); 

     using (SqlConnection conn = ConnectionManager.DataHubConnection()) 
     { 
      string sql = "SELECT customer.AccountNumber " 
        + ", customer.CustomerStartDate " 
        + ", MAX(invoice.InvoiceDate) as LastInvoice " 
        + ", MAX(orders.OrderDate) AS LastOrder " 
       + "FROM Datahub..InvoiceHold as invoice " 
        + "INNER JOIN Datahub..CustomerBase as customer ON invoice.AccountNumber = customer.AccountNumber " 
        + "INNER JOIN Datahub..OrderHold as orders ON invoice.AccountNumber = orders.AccountNumber " 
       + "WHERE customer.CoNo = '003' " 
        + "AND customer.CustomerStartDate BETWEEN GETDATE() - 1095 AND GETDATE() " 
        + marketChannel 
        + businessUnit 
       + "GROUP BY customer.AccountNumber, customer.CustomerStartDate " 
       + "ORDER BY LastInvoice "; 

      using (SqlDataAdapter da = new SqlDataAdapter(sql, conn)) 
      { 
       da.Fill(dt); 
      } 
     } 

     string foo = string.Empty; 
     foreach (DataRow row in dt.Rows) 
     { 
      myClass = new CustomerClass(); 
      myClass.AccountNumber = row["AccountNumber"].ToString(); 
      myClass.CustomerStartDate = row["CustomerStartDate"] == DBNull.Value ? new DateTime(1900,1,1) : Convert.ToDateTime(row["CustomerStartDate"]); 
      myClass.LastInvoiceDate = row["LastInvoice"] == DBNull.Value ? new DateTime(1900, 1, 1) : Convert.ToDateTime(row["LastInvoice"]); 
      myClass.LastOrderDate = row["LastOrder"] == DBNull.Value ? "1-1-1900" : Convert.ToDateTime(row["LastOrder"]).ToString("d"); 
      myList.Add(myClass); 
     } 

     // Use LINQ to break this data into series and then write string 

     // How many weeks do we need 
     int weeks = 1; 
     switch (timeframe) 
     { 
      case "Last Year": 
       weeks = 104; 
       break; 
      case "This Year": 
       weeks = 52; 
       break; 
      case "Last Quarter": 
       weeks = 26; 
       break; 
      case "This Quarter": 
       weeks = 13; 
       break; 
      case "Last Month": 
       weeks = 8; 
       break; 
      case "This Month": 
       weeks = 4; 
       break; 
      case "Last Week": 
       weeks = 2; 
       break; 
      case "This Week": 
       weeks = 1; 
       break; 
     } 


     object[] myArray = { "Class", "Lost", "Active", "Prospect" }; 
     retVal.Add(myArray); 


     for (int i = 1; i <= weeks; i++) 
     { 
      var dateOffset = -(i * 7); 
      DateTime date = DateTime.Today.AddDays(dateOffset); 

      var prospect = 
       from c in myList 
       where c.CustomerStartDate <= date 
        && c.LastInvoiceDate > date 
        && c.LastOrderDate > date 
       select c; 
      var prospectCount = prospect.Count(); 

      var active = 
       from c in myList 
       where c.CustomerStartDate <= date 
        && c.LastInvoiceDate >= date.AddDays(-365) 
       select c; 
      var activeCount = active.Count(); 

      var lost = 
       from c in myList 
       where c.CustomerStartDate <= date 
        && c.LastInvoiceDate <= date.AddDays(-365) 
        && c.LastInvoiceDate >= date.AddDays(-730) 
       select c; 
      var lostCount = lost.Count(); 

      myArray = new object[] { i, lostCount, activeCount, prospectCount }; 
      retVal.Add(myArray); 

     } 

     return retVal; 
    } 
0

Чтобы создать временную таблицу сколько угодно недель, вы хотите вернуться назад и использовать группу для разбивки своих данных.

Это позволит сделать временную таблицу с номерами недель (в данном примере составляет 1 недели - 150)

declare @digits table 
(
    digit int 
) 
insert into @digits values (1) 
insert into @digits values (2) 
insert into @digits values (3) 
insert into @digits values (4) 
insert into @digits values (5) 
insert into @digits values (6) 
insert into @digits values (7) 
insert into @digits values (8) 
insert into @digits values (9) 
insert into @digits values (0) 

declare @weeks table 
(
    weekNumber int 
) 
insert into @weeks 
select a.digit * 100 + b.digit * 10 + c.digit 
from @digits a 
cross join @digits b 
cross join @digits c 
where a.digit * 100 + b.digit * 10 + c.digit <= 150 
and a.digit * 100 + b.digit * 10 + c.digit > 0 

Тогда вы могли бы сделать что-то подобное в вашем запросе:

SELECT w.weekNumber, COUNT(distinct customer.AccountNumber) 
FROM Datahub..InvoiceHold invoice 
    INNER JOIN Datahub..CustomerBase as customer ON invoice.AccountNumber = customer.AccountNumber 
    INNER JOIN Datahub..OrderHold as orders ON invoice.AccountNumber = orders.AccountNumber 
    INNER JOIN @weeks w ON customer.CustomerStartDate <= DATEADD(dd, -(w.weekNumber * 7), GetDate()) 
       AND invoice.InvoiceDate > DATEADD(dd, -(w.weekNumber * 7), GetDate()) 
       AND orders.OrderDate > DATEADD(dd, -(w.weekNumber * 7), GetDate()) 
WHERE customer.CoNo = '003' 
GROUP BY w.weekNumber 
ORDER BY w.weekNumber 
0

Я думаю, что вам нужна таблица с волшебными цифрами. В Postgres вы можете создать псевдо-таблицу с generate_series.

SELECT gs, COUNT(distinct customer.AccountNumber) 
FROM Datahub..InvoiceHold invoice 
    INNER JOIN Datahub..CustomerBase as customer 
    ON invoice.AccountNumber = customer.AccountNumber 
    INNER JOIN Datahub..OrderHold as orders 
    ON invoice.AccountNumber = orders.AccountNumber 
    JOIN generate_series(1, 10) AS gs /* 10?? no on clause, cross join */ 
WHERE customer.CoNo = '003' 
    AND customer.CustomerStartDate <= GETDATE() - 7*gs 
    AND invoice.InvoiceDate > GETDATE() - 7*gs 
    AND orders.OrderDate > GETDATE() - 7*gs 
ORDER BY 1,2 /* column numbers */; 

Если SQL Server не имеет этого, просто сделать таблицу с 1 до 10 (или любой другой) и кросс присоединиться к этому.

 Смежные вопросы

  • Нет связанных вопросов^_^