简体   繁体   中英

SQL grouping results, sub-total and grand-total

I'm looking for help with SQL that I'm completely stuck with. Still relatively new...

Here's what I'm doing at the moment:

private void FillSalesGrid()
    {            
        using (SqlConnection con = new SqlConnection(conn))
        {
            sqlBuilder.Append("SELECT FORMAT(date, 'd', 'en-gb') AS Date, saleID AS [Invoice ID], Patient.firstName + ' ' + Patient.lastName AS [Name], description AS Description,  saleType AS [Type of Sale], saleAmount AS [Amount (R)] FROM Sale LEFT JOIN Patient ON Sale.patientIDNumber = Patient.patientIDNumber WHERE 1=1");

            if (!string.IsNullOrEmpty(comboBox_selectSaleType.Text))
            {
                try
                {
                    sqlBuilder.Append(" AND saleType = @saleType");
                    cParameters.Add(new SqlParameter("@saleType", comboBox_selectSaleType.SelectedItem.ToString()));
                }
                catch
                {
                    MessageBox.Show("no results");
                }
                if (comboBox_selectSaleType.Text == "All Sales")
                {
                    sqlBuilder.Remove(sqlBuilder.Length - 25, 25);
                }
            }

            if (!string.IsNullOrEmpty(datePicker_StartDate.Text) || !string.IsNullOrEmpty(datePicker_EndDate.Text))
            {

                if (!string.IsNullOrEmpty(datePicker_StartDate.Text) && string.IsNullOrEmpty(datePicker_EndDate.Text))
                {
                    sqlBuilder.Append(" AND date > @startDate");
                    cParameters.Add(new SqlParameter("@startDate", datePicker_StartDate.Text));

                }
                else if (string.IsNullOrEmpty(datePicker_StartDate.Text) && !string.IsNullOrEmpty(datePicker_EndDate.Text))
                {
                    sqlBuilder.Append(" AND date < @endDate");

                    cParameters.Add(new SqlParameter("@endDate", datePicker_EndDate.Text));
                }
                else
                {
                    sqlBuilder.Append(" AND date BETWEEN @startDate AND @endDate");
                    cParameters.Add(new SqlParameter("@startDate", datePicker_StartDate.Text));
                    cParameters.Add(new SqlParameter("@endDate", datePicker_EndDate.Text));
                }
            }
            if (!string.IsNullOrEmpty(comboBox_select_Item.Text))
            {
                sqlBuilder.Append(" AND Description LIKE @medName + '%'");
                cParameters.Add(new SqlParameter("@medName", comboBox_select_Item.SelectedItem.ToString()));
            }
            if (!string.IsNullOrEmpty(textBox_PatientIDSelect.Text))
            {
                sqlBuilder.Append(" AND Sale.patientIDNumber = @patientID");
                cParameters.Add(new SqlParameter("@patientID", textBox_PatientIDSelect.Text));
            }

            sqlBuilder.Append(" ORDER BY Sale.date");

            SqlCommand cmd = new SqlCommand(sqlBuilder.ToString(), con);
            if (cParameters.Count != 0)
            {
                cmd.Parameters.AddRange(cParameters.ToArray());
            }

            SqlDataAdapter da = new SqlDataAdapter(cmd);
            dt = new DataTable("Sale");
            da.Fill(dt);
            // totalSales(dt);
            sqlBuilder.Clear();
            cParameters.Clear();
            dataGrid_Reports.ItemsSource = dt.DefaultView;
    }

I'm pulling data from my Sales table for a sales report type functionality. I'm looking to improve that to make it more readable/meaningful. As you can see, I'm building the SQL statement with parameters based on input from the user. This is probably not the best way to do it.

Here's an example of how the results look, on form load, showing my SALES table.

+----------+-------------+-----------+--------------+--------------+--------
|   date   |  Invoice ID |   Name    |  Description |  Sale Type   | Amount 
+--------- +-------------+-----------+--------------+--------------+--------
| 01/02/91 |    1        |   Dean    |   Panado     |  Cash        |  50    
| 02/02/91 |    3        |   Chris   |   Oralox     |  Cash        |  60    
| 03/02/91 |    5        |   Peter   |   Zadin      |  Card        |  99    
| 05/02/91 |    6        |   John    |   Illiadin   |  Medical Aid |  85    
| 08/02/91 |    8        |   Mike    |   Betamine   |  Cash        |  129   
+----------+-------------+-----------+--------------+--------------+--------+

The results will be 'filtered' based on the dates entered, patientID entered, med name or type of sale.

Ideally I want to have something like this (patientID is linked to their name):

Name      InvoiceID    Date         Description   Type of Sale   Amount              
John Doe       1       01/02/2009      Panado        Cash          50
               3       02/02/2009      Panado        Cash          50
               5       03/02/2009      Disprin    Medical Aid      99

Sub-Total                                                         R199

For each patient - and then a grand-total at the end to sum up all the sub-totals.

Any assistance here would absolutely be appreciated. Thank you.

Assuming from the question that your table have sample data as below.

date           Invoice_ID   Name     Description     Sale_Type      Amount
---------------------------------------------------------------------------
02.01.1991        1        John       Panado          Cash            50
02.02.1991        3        John       Oralox          Cash            60
02.03.1991        5        John       Zadin           Card            99
02.05.1991        6        John       Illiadin        Medical Aid     85
02.08.1991        8        John       Betamine        Cash            129

and you need to return all rows along with sub-total , you can use rollup function as below to generate your desired result.

SELECT CASE
           WHEN (GROUPING(t1.name) = 1) THEN 'Sub-Total'
           ELSE ISNULL(t1.name, 'UNKNOWN')
       END AS Name,
       t1.date,
       t1.invoice_id,
       t1.description,
       t1.sale_type,
       sum(t1.Amount) as Amount
FROM t1
GROUP BY rollup((t1.date,t1.invoice_id,t1.name,t1.description,t1.sale_type));

Result:

Name                  date          invoice_id  description    sale_type      Amount
------------------------------------------------------------------------------------
John          02.01.1991 00:00:00       1         Panado         Cash           50
John          02.02.1991 00:00:00       3         Oralox         Cash           60
John          02.03.1991 00:00:00       5         Zadin          Card           99
John          02.05.1991 00:00:00       6         Illiadin       Medical Aid    85
John          02.08.1991 00:00:00       8         Betamine       Cash           129
Sub-Total                                                                       423

If you just need to show name in first row only, you have to use the above query as inner query and use another case in outer query as below.

SELECT CASE
           WHEN row_number() over(partition BY name
                                  ORDER BY name ASC) =1 THEN name
           ELSE NULL
       END, date, invoice_id,
                  description,
                  sale_type,
                  Amount
FROM
  (SELECT CASE
              WHEN (GROUPING(t1.name) = 1) THEN 'Sub-Total'
              ELSE ISNULL(t1.name, 'UNKNOWN')
          END AS Name,
          t1.date,
          t1.invoice_id,
          t1.description,
          t1.sale_type,
          sum(t1.Amount) AS Amount
   FROM t1
   GROUP BY rollup((t1.date,t1.invoice_id,t1.name,t1.description,t1.sale_type)) ) t;

Result:

Name                  date          invoice_id  description    sale_type      Amount
------------------------------------------------------------------------------------
John          02.01.1991 00:00:00       1         Panado         Cash           50
              02.02.1991 00:00:00       3         Oralox         Cash           60
              02.03.1991 00:00:00       5         Zadin          Card           99
              02.05.1991 00:00:00       6         Illiadin       Medical Aid    85
              02.08.1991 00:00:00       8         Betamine       Cash           129
Sub-Total                                                                       423

You can check the demo here

Hope this will help :-).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM