SQL Server : SUM() of multiple rows including where clauses - sql

I have a table that looks something like the following :
PropertyID Amount Type EndDate
--------------------------------------------
1 100 RENT null
1 50 WATER null
1 60 ELEC null
1 10 OTHER null
2 70 RENT null
2 10 WATER null
There will be multiple items billed to a property, also billed multiple times. For example RENT could be billed to property #1 12 times (over a year), however the only ones I'm interested for are those with ENDDATE of null (in otherwords, current)
I would like to achieve :
PropertyId Amount
--------------------------
1 220
2 80
I have tried to do something like this :
SELECT
propertyId,
SUM() as TOTAL_COSTS
FROM
MyTable
However, in the SUM would I be forced to have multiple selects bringing back the current amount for each type of charge? I could see this becoming messy and I'm hoping for a much simpler solution
Any ideas?

This will bring back totals per property and type
SELECT PropertyID,
TYPE,
SUM(Amount)
FROM yourTable
GROUP BY PropertyID,
TYPE
This will bring back only active values
SELECT PropertyID,
TYPE,
SUM(Amount)
FROM yourTable
WHERE EndDate IS NULL
GROUP BY PropertyID,
TYPE
and this will bring back totals for properties
SELECT PropertyID,
SUM(Amount)
FROM yourTable
WHERE EndDate IS NULL
GROUP BY PropertyID
......

Try this:
SELECT
PropertyId,
SUM(Amount) as TOTAL_COSTS
FROM
MyTable
WHERE
EndDate IS NULL
GROUP BY
PropertyId

you mean getiing sum(Amount of all types) for each property where EndDate is null:
SELECT propertyId, SUM(Amount) as TOTAL_COSTS
FROM MyTable
WHERE EndDate IS NULL
GROUP BY propertyId

sounds like you want something like:
select PropertyID, SUM(Amount)
from MyTable
Where EndDate is null
Group by PropertyID

The WHERE clause is always conceptually applied (the execution plan can do what it wants, obviously) prior to the GROUP BY. It must come before the GROUP BY in the query, and acts as a filter before things are SUMmed, which is how most of the answers here work.
You should also be aware of the optional HAVING clause which must come after the GROUP BY. This can be used to filter on the resulting properties of groups after GROUPing - for instance HAVING SUM(Amount) > 0

Use a common table expression to add grand total row, top 100 is required for order by to work.
With Detail as
(
SELECT top 100 propertyId, SUM(Amount) as TOTAL_COSTS
FROM MyTable
WHERE EndDate IS NULL
GROUP BY propertyId
ORDER BY TOTAL_COSTS desc
)
Select * from Detail
Union all
Select ' Total ', sum(TOTAL_COSTS) from Detail

Related

SQL - Only one result per set

I have a SQL problem (MS SQL Server 2012), where I only want one result per set, but have different items in some rows, so a group by doesn't work.
Here is the statement:
Select Deliverer, ItemNumber, min(Price)
From MyTable
Group By Deliverer, ItemNumber
So I want the deliverer with the lowest price for one item.
With this query I get the lowest price for each deliverer.
So a result like:
DelA 12345 1,25
DelB 11111 2,31
And not like
DelA 12345 1,25
DelB 12345 1,35
DelB 11111 2,31
DelC 11111 2,35
I know it is probably a stupid question with an easy solution, but I tried for about three hours now and just can't find a solution. Needles to say, I'm not very experienced with SQL.
Just Add an aggregate function to your deliverer field also, as appropriate (Either min or max). From your data, I guess you need min(deliverer) and hence use the below query to get your desired result.
Select mIN(Deliverer), ItemNumber, min(Price)
From MyTable
Group By ItemNumber;
EDIT:
Below query should help you get the deliverer with the lowest price item-wise:
SELECT TABA.ITEMNUMBER, TABA.MINPRICE, TABB.DELIVERER
FROM
(
SELECT ITEMNUMBER, MIN(PRICE) MINPRICE
FROM MYTABLE GROUP BY
ITEMNUMBER
) TABA JOIN
MYTABLE TABB
ON TABA.ITEMNUMBER=TABB.ITEMNUMBER AND
TABA.MINPRICE = TABB.PRICE
You should be able to do this with the RANK() (or DENSE_RANK()) functions, and a bit of partitioning, so something like:
; With rankings as (
SELECT Deliverer,
rankings.ItemNumber,
rankings.Price
RANK() OVER (PARTITION BY ItemNumber ORDER BY Price ASC) AS Ranking
FROM MyTable (Deliverer, ItemNumber, Price)
)
SELECT rankings.Deliverer,
rankings.ItemNumber,
rankings.Price
FROM rankings
WHERE ranking = 1

Selecting 5 Most Recent Records Of Each Group

The below statement retrieves the top 2 records within each group in SQL Server. It works correctly, however as you can see it doesn't scale at all. I mean that if I wanted to retrieve the top 5 or 10 records instead of just 2, you can see how this query statement would grow very quickly.
How can I convert this query into something that returns the same records, but that I can quickly change it to return the top 5 or 10 records within each group instead, rather than just 2? (i.e. I want to just tell it to return the top 5 within each group, rather than having 5 unions as the below format would require)
Thanks!
WITH tSub
as (SELECT CustomerID,
TransactionTypeID,
Max(EventDate) as EventDate,
Max(TransactionID) as TransactionID
FROM Transactions
WHERE ParentTransactionID is NULL
Group By CustomerID,
TransactionTypeID)
SELECT *
from tSub
UNION
SELECT t.CustomerID,
t.TransactionTypeID,
Max(t.EventDate) as EventDate,
Max(t.TransactionID) as TransactionID
FROM Transactions t
WHERE t.TransactionID NOT IN (SELECT tSub.TransactionID
FROM tSub)
and ParentTransactionID is NULL
Group By CustomerID,
TransactionTypeID
Use Partition by to solve this type problem
select values from
(select values ROW_NUMBER() over (PARTITION by <GroupColumn> order by <OrderColumn>)
as rownum from YourTable) ut where ut.rownum<=5
This will partitioned the result on the column you wanted order by EventDate Column then then select those entry having rownum<=5. Now you can change this value 5 to get the top n recent entry of each group.

Finding the total amount for my query

I have a query that allows me to retrieve customer orders.
Select ID, OrderID, Item, Price from customerOrders
Where Order = 1
is there a way of totalling up the final amount to pay and have it visible below, but still be able to view all details needed in the sql query, for example
ID | OrderID | Item | Price
1 | 1 | Book | 9.99
2 | 1 | DVD | 12.99
total = 22.98
Many thanks in advance
Two steps:
1, To generate the SUM, you need to add it to the result set using UNION ALL. UNION adds a DISTINCT operation which isn't required, even if in your case it won't change the result.
Select ID, OrderID, Item, Price from customerOrders
Where [Order] = 1
UNION ALL
Select NULL, NULL, NULL, SUM(Price) from customerOrders
Where [Order] = 1
ORDER BY CASE WHEN ID IS NULL THEN 2 ELSE 1 END, ID;
2, To sort it in the order you want (sum at the bottom), you can use the NULL ID as an identifier.
You can try COMPUTE
Select ID, OrderID, Item, Price from customerOrders
Where Order = 1
COMPUTE SUM(Price)
Technically, the thing you want is called a roll-up row. SQL Server has a special function for such cases, it's called ROLLUP() and used inside a GROUP BY clause. In your particular situation, the query would basically look like this:
SELECT ID, OrderID, Item, SUM(Price) AS Price
FROM customerOrders
WHERE OrderID = 1
GROUP BY ROLLUP((ID, OrderID, Item))
;
You can see a live demonstration of this query at SQL Fiddle. More information about ROLLUP(), as well as other GROUP BY functions, can be found at MSDN.
This is what you need:
Select ID, OrderID, Item, Price from customerOrders
Where Order = 1
Union
Select NULL, NULL, NULL, (
Select Sum(Price) from customerOrders
Where Order = 1
Group By Order
)
UPDATE
Incase the result was empty, the total should be zero, not empty. Use the updated code.
Cheers

See whether an item appears more than once in a database column

I want to check if a piece of data appears more than once in a particular column in my table using SQL. Here is my SQL code of what I have so far:
select * from AXDelNotesNoTracking where count(salesid) > 1
salesid is the column I wish to check for, any help would be appreciated, thanks.
It should be:
SELECT SalesID, COUNT(*)
FROM AXDelNotesNoTracking
GROUP BY SalesID
HAVING COUNT(*) > 1
Regarding your initial query:
You cannot do a SELECT * since this operation requires a GROUP BY
and columns need to either be in the GROUP BY or in an aggregate
function (i.e. COUNT, SUM, MIN, MAX, AVG, etc.)
As this is a GROUP BY operation, a HAVING clause will filter it
instead of a WHERE
Edit:
And I just thought of this, if you want to see WHICH items are in there more than once (but this depends on which database you are using):
;WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY SalesID ORDER BY SalesID) AS [Num]
FROM AXDelNotesNoTracking
)
SELECT *
FROM cte
WHERE cte.Num > 1
Of course, this just shows the rows that have appeared with the same SalesID but does not show the initial SalesID value that has appeared more than once. Meaning, if a SalesID shows up 3 times, this query will show instances 2 and 3 but not the first instance. Still, it might help depending on why you are looking for multiple SalesID values.
Edit2:
The following query was posted by APC below and is better than the CTE I mention above in that it shows all rows in which a SalesID has appeared more than once. I am including it here for completeness. I merely added an ORDER BY to keep the SalesID values grouped together. The ORDER BY might also help in the CTE above.
SELECT *
FROM AXDelNotesNoTracking
WHERE SalesID IN
( SELECT SalesID
FROM AXDelNotesNoTracking
GROUP BY SalesID
HAVING COUNT(*) > 1
)
ORDER BY SalesID
How about:
select salesid from AXDelNotesNoTracking group by salesid having count(*) > 1;
To expand on Solomon Rutzky's answer, if you are looking for a piece of data that shows up in a range (i.e. more than once but less than 5x), you can use
having count(*) > 1 and count(*) < 5
And you can use whatever qualifiers you desire in there - they don't have to match, it's all just included in the 'having' statement.
https://webcheatsheet.com/sql/interactive_sql_tutorial/sql_having.php
try this:
select salesid,count (salesid) from AXDelNotesNoTracking group by salesid having count (salesid) >1

Return min date and corresponding amount to that distinct ID

Afternoon
I am trying to return the min value/ max values in SQL Server 2005 when I have multiple dates that are the same but the values in the Owed column are all different. I've already filtered the table down by my select statement into a temp table for a different query, when I've then tried to mirror I have all the duplicated dates that you can see below.
I now have a table that looks like:
ID| Date |Owes
-----------------
1 20110901 89
1 20110901 179
1 20110901 101
1 20110901 197
1 20110901 510
2 20111001 10
2 20111001 211
2 20111001 214
2 20111001 669
My current query:
Drop Table #Temp
Select Distinct Convert(Varchar(8), DateAdd(dd, Datediff(DD,0,DateDue),0),112)as Date
,ID
,Paid
Into #Temp
From Table
Where Paid <> '0'
Select ,Id
,Date
,Max(Owed)
,Min(Owed)
From #Temp
Group by ID, Date, Paid
Order By ID, Date, Paid
This doesn't strip out any of my dates that are the same, I'm new to SQL but I'm presuming its because my owed column has different values. I basically want to be able to pull back the first record as this will always be my minimum paid and my last record will always be my maximum owed to work out my total owed by ID.
I'm new to SQL so would like to understand what I've done wrong for my future knowledge of structuring queries?
Many Thanks
In your "select into"statement, you don't have an Owed column?
GROUP BY is the normal way you "strip out values that are the same". If you group by ID and Date, you will get one row in your result for each distinct pair of values in those two columns. Each row in the results represents ALL the rows in the underlying table, and aggregate functions like MIN, MAX, etc. can pull out values.
SELECT id, date, MAX(owes) as MaxOwes, MIN(owes) as minOwes
FROM myFavoriteTable
GROUP BY id, date
In SQL Server 2005 there are "windowing functions" that allow you to use aggregate functions on groups of records, without grouping. An example below. You will get one row for each row in the table:
SELECT id, date, owes,
MAX(Owes) over (PARTITION BY select, id) AS MaxOwes,
MIN(Owes) over (PARTITION BY select, id) AS MinOwes
FROM myfavoriteTable
If you name a column "MinOwes" it might sound like you're just fishing tho.
If you want to group by date you can't also group by ID, too, because ID is probably unique. Try:
Select ,Date
,Min(Owed) AS min_date
,Max(Owed) AS max_date
From #Temp
Group by Date
Order By Date
To get additional values from the row (your question is a bit vague there), you could utilize window functions:
SELECT DISTINCT
,Date
,first_value(ID) OVER (PARTITION BY Date ORDER BY Owed) AS min_owed_ID
,last_value(ID) OVER (PARTITION BY Date ORDER BY Owed) AS max_owed_ID
,first_value(Owed) OVER (PARTITION BY Date ORDER BY Owed) AS min_owed
,last_value(Owed) OVER (PARTITION BY Date ORDER BY Owed) AS max_owed
FROM #Temp
ORDER BY Date;