SQL sum field when column values match - sql

The title was hard to word but the question is pretty simple. I searched all over here and could not find something for my specific issue so here it is. I'm usuing Microsoft SQL Server Management Studio 2010.
Table Currently looks like this
| Value | Product Name|
| 300 | Bike |
| 400 | Bike |
| 300 | Car |
| 300 | Car |
I need the table to show me the sum of Values where Product Name matches - like this
| TOTAL | ProductName |
| 700 | Bike |
| 600 | Car |
I've tried a simple
SELECT
SUM(Value) AS 'Total'
,ProductName
FROM TableX
But the above doesn't work. I end up getting the sum of all values in the column. How can I sum based on the product name matching?
Thanks!

SELECT SUM(Value) AS 'Total', [Product Name]
FROM TableX
GROUP BY [Product Name]
SQL Fiddle Example

Anytime you use an aggregate function, (SUM, MIN, MAX ... ) with a column in the SELECT statement, you must use GROUP BY. This is a group function that indicates which column to group the aggregate by. Further, any columns that are not in the aggregate cannot be in your SELECT statement.
For example, the following syntax is invalid because you are specifying columns (col2) which are not in your GROUP BY (even though MySQL allows for this):
SELECT col1, col2, SUM(col3)
FROM table
GROUP BY col1
The solution to your question would be:
SELECT ProductName, SUM(Value) AS 'Total'
FROM TableX
GROUP BY ProductName

Related

How to add a total row at the end of the table in t-sql?

I need to add a row of sums as the last row of the table. For example:
book_name | some_row1 | some_row2 | sum
---------------+---------------+---------------+----------
book1 | some_data11 | some_data12 | 100
book2 | some_data21 | some_data22 | 300
book3 | some_data31 | some_data32 | 500
total_books=3 | NULL | NULL | 900
How can I do this? (T-SQL)
You can use union all :
select book_name, some_row1, some_row2, sum
from table t
union all
select cast(count(*) as varchar(255)), null, null, sum(sum)
from table t;
However, count(*) will give you no of rows available in table, if the book_name has null value also, then you need count(book_name) instead of count(*).
Try with ROLLUP
SELECT CASE
WHEN (GROUPING([book_name]) = 1) THEN 'total_books'
ELSE [book_name] END AS [book_name],some_row1, some_row2
,SUM(]sum]) as Total_Sales
From Before
GROUP BY
[book_name] WITH ROLLUP
I find that grouping sets is much more flexible than rollup. I would write this as:
select coalesce(book_name,
replace('total_books=#x', '#x', count(*))
) as book_name,
col2, col3, sum(whatever)
from t
group by grouping sets ( (book_name), () );
Strictly speaking, the GROUPING function with a CASE is better than COALESCE(). However, NULL values on the grouping keys is quite rare.

Oracle issue in group by

I know this question is asked by many people. But I still couldn't figure out why this is happening. Couldn't understand this logic.
I have a table mytesttable with columns id, company_name and employee_name.
I am trying to get the employee details grouping them with respect to company name. So I used the below query:
select *
from mytesttable
group by company_name;
But I get the below issue:
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
Error at Line: 28 Column: 2
Now I have tried putting count(1) in select along with my columns, tried grouping using 2 columns etc. Still the same issue. Can anyone explain me how to achieve this?
Because this is a simple group by. Logic seems to be right, but wondering why it's not fetching me the result.
Your query is:
select *
from mytesttable
group by company_name;
The * expands to all the columns. So this becomes:
select company_name, col1, col2, col3, . . . -- your question doesn't specify the column names
from mytesttable
group by company_name;
When you specify the group by, you are specifying that there is one row per company_name in the result set. The other columns are normally filled with aggregation functions, such as MIN(), SUM(), or LISTAGG().
What value should be chosen for col1? In general, SQL does not attempt to answer this question. Instead, it returns a syntax error. This is not Oracle-specific. This is the definition of the language.
What you probably want is:
select company_name, count(*) as num_employees
from mytesttable
group by company_name;
In group by clause you have to put all selected columns. As you select all but given only one column in group by as a result it thrown error, but if you simply try like this way it will work. Actually group by used for aggregated function
select company_name, count(*) from mytesttable group by company_name;
I think you mis-understand what GROUP BY does?
It doesn't put all similar records next to each other, that's ORDER BY. What GROUP BY does is to collapse all similar records in to the same row, to allow aggregate calculations like SUM() and MIN() and COUNT(), etc.
So, two examples using the same input...
id | company_name | employee_name
----+--------------+---------------
1 | zzz | aaa
2 | xxx | bbb
3 | yyy | ccc
4 | zzz | ddd
5 | xxx | eee
Using ORDER BY...
SELECT * FROM mytesttable ORDER BY company_name, employee_name
id | company_name | employee_name
----+--------------+---------------
2 | xxx | bbb
5 | xxx | eee
3 | yyy | ccc
1 | zzz | aaa
4 | zzz | ddd
Using GROUP BY...
SELECT
company_name,
COUNT(*) number_of_employees,
MAX(id) highest_id_in_company
FROM
mytesttable
GROUP BY
company_name
ORDER BY
company_name
company_name | number_of_employees | highest_id_in_company
--------------+---------------------+-----------------------
xxx | 2 | 5
yyy | 1 | 3
zzz | 2 | 4
If you use group-by expression,you have to use aggregate functions such as max,min,average,count in select statements.
For example;
select count(*),company_name,employee_name
from mytesttable
group by company_name,employee_name
order by company_name;

Redshift does not support rollup(), grouping() functions

Trying to convert Teradata bteq SQL scripts to redshift SQL. My current redshift Postgres version is 8.0.2, redshift version is 1.0.1499. The current version of redshift does not support rollup(), grouping() functions. How to overcome and resolve this scenario. What are the equivalent redshift functions for them? Could anyone explain with some examples how to do?
Sample Teradata SQL-
select
PRODUCT_ID,CUST_ID,
GROUPING (PRODUCT_ID),
GROUPING (CUST_ID),
row_number over (order by PRODUCT_ID,CUST_ID) AS "ROW_OUTPUT_NUM"
from products
group by rollup(PRODUCT_ID,CUST_ID);
Need to convert above sql query to Redshift
Implement the ROLLUP by hand
Once Redshift does not currently recognize the ROLLUP clause, you must implement this grouping technique in a hard way.
ROLLUP with 1 argument
With ROLLUP Ex. PostgreSQL
SELECT column1, aggregate_function(*)
FROM some_table
GROUP BY ROLLUP(column1)
The equivalent implementation
-- First, the same GROUP BY without the ROLLUP
-- For efficiency, we will reuse this table
DROP TABLE IF EXISTS tmp_totals;
CREATE TEMP TABLE tmp_totals AS
SELECT column1, aggregate_function(*) AS total1
FROM some_table
GROUP BY column1;
-- Show the table 'tmp_totals'
SELECT * FROM tmp_totals
UNION ALL
-- The aggregation of 'tmp_totals'
SELECT null, aggregate_function(total1) FROM tmp_totals
ORDER BY 1
Example output
Country | Sales
-------- | -----
Poland | 2
Portugal | 4
Ukraine | 3
null | 9
ROLLUP with 2 argument
With ROLLUP Ex. PostgreSQL
SELECT column1, column2, aggregate_function(*)
FROM some_table
GROUP BY ROLLUP(column1, column2);
The equivalent implementation
-- First, the same GROUP BY without the ROLLUP
-- For efficiency, we will reuse this table
DROP TABLE IF EXISTS tmp_totals;
CREATE TEMP TABLE tmp_totals AS
SELECT column1, column2, aggregate_function(*) AS total1
FROM some_table
GROUP BY column1, column2;
-- Show the table 'tmp_totals'
SELECT * FROM tmp_totals
UNION ALL
-- The sub-totals of the first category
SELECT column1, null, sum(total1) FROM tmp_totals GROUP BY column1
UNION ALL
-- The full aggregation of 'tmp_totals'
SELECT null, null, sum(total1) FROM tmp_totals
ORDER BY 1, 2;
Example output
Country | Segment | Sales
-------- | -------- | -----
Poland | Premium | 0
Poland | Base | 2
Poland | null | 2 <- sub total
Portugal | Premium | 1
Portugal | Base | 3
Portugal | null | 4 <- sub total
Ukraine | Premium | 1
Ukraine | Base | 2
Ukraine | null | 3 <- sub total
null | null | 9 <- grand total
If you use the UNION technique that others have pointed to, you'll be scanning the underlying table multiple times.
If the fine-level GROUPing actually results in a significant reduction in the data size, a better solution may be:
create temp table summ1
as
select PRODUCT_ID,CUST_ID, ...
from products
group by PRODUCT_ID,CUST_ID;
create temp table summ2
as
select PRODUCT_ID,cast(NULL as INT) AS CUST_ID, ...
from products
group by PRODUCT_ID;
select * from summ1
union all
select * from summ2
union all
select cast(NULL as INT) AS PRODUCT_ID, cast(NULL as INT) AS CUST_ID, ...
from summ2

"Transpose" of a table in Oracle

I'm having quite a bit of trouble figuring out exactly how to rearrange a table. I have a large table that looks something like this:
+--------+-----------+
| NAME | ACCOUNT # |
+--------+-----------+
| Nike | 87 |
| Nike | 12 |
| Adidas | 80 |
| Adidas | 21 |
+--------+-----------+
And I want to rearrange it to look like this:
+------+--------+
| Nike | Adidas |
+------+--------+
| 87 | 80 |
| 12 | 21 |
+------+--------+
But I can't seem to figure out how. I tried using PIVOT, but that only works with aggregate functions. I tried using a FOR LOOP as well, but couldn't get it work just right.
You can do this in several ways, but all being by enumerating the rows. Here is an example using conditional aggregation:
select max(case when name = 'Nike' then account end) as Nike,
max(case when name = 'Adidas' then account end) as Adidas
from (select t.*,
row_number() over (partition by name order by account desc) as seqnum
from t
) t
group by seqnum;
Consider again a pivot solution but first adding a rownumber for rolling Name group counts. Below assumes an autonumber ID field:
SELECT * FROM
(
SELECT Name, "Account #",
(ROW_NUMBER() OVER(PARTITION BY Name ORDER BY ID)) GrpRowNum
/* ALT: (SELECT Count(*) FROM Table1 sub
* WHERE sub.Name = Table1.Name AND sub.ID <= Table1.ID) GrpRowNum */
FROM Table1
)
PIVOT
(
SUM("Account #")
FOR Name IN ('Nike', 'Adidas')
)
ORDER BY RowNum;
However, for your ~200 items, you cannot easily render the Pivot's IN clause without various workarounds including PIVOT XML output or stored procedures with PL/SQL. Similarly, you could use general purpose coding (Java, PHP, Python, R) to retreive SELECT DISTINCT Name FROM Table1 resultset in vector/array, joining element values (collapsing or imploding arrays) with quotes and comma separators, and dropping the entire list in IN clause.

Comparing in SQL and SUM

I really couldn't figure out a good title for this question, but I have a problem that I'm sure you can help me with!
I have a query which outputs something like this:
Month | Year | Subcategory | PrivateLabel | Price
-------------------------------------------------
1 | 2010 | 666 | No | -520
1 | 2010 | 666 | No | -499,75
1 | 2010 | 666 | No | -59,95
1 | 2010 | 666 | No | -49,73
1 | 2010 | 666 | No | -32,95
I want to SUM on the price because all the other data is the same. I thought I could do this with SUM and GROUP BY, but I can't figure out how to do it or at least it doesn't output the right result.
The query is an inner join between two tables, if that helps.
select
month
,year
,subcategory
,privatelabel
,sum(price) as [total sales]
from
a inner join b ...
where
any where clauses
group by
month
,year
,subcategory
,privatelabel
should work if i am understanding you correctly.. every colum in the select either needs to be part of the group by or an aggregate function on all rows in the group
added a fiddle.. mainly as i didn't know about he text to DDL functionality and wanted to test it ;-) (thanks Michael Buen)
http://sqlfiddle.com/#!3/35c1c/1
note the where clause is a place holder..
select month, year, subcategory, privatelabel, sum(price)
from (put your query in here) dummyName
group by month, year, subcategory, privatelabel
Basic idea is it will run your current query to get above output then do the sum and group by on the result.
You query has to be in parentheses and you have to give it some name e.g. dummyName. As long as it's unique in the sql and preferably not a key word, doesn't matter what it is.
There might be a way of doing all this in one go, but without the sql for your query we can't help.