Access 2016 & SQL: Totaling two columns, then subtracting them - sql

Say I have a MoneyIN and a MoneyOUT column. I wish to total these entire columns up so I have a sum of each, then I wish to subtract the total of the MoneyOUT column from the total of the MoneyIN column. I also want to display a DateOF column and possibly a description (I think I can do that by myself).
This would be the original database where I get my information from:
+-------------+------------------+---------+----------+-----------+
| Location ID | Location Address | Date Of | Money In | Money Out |
+-------------+------------------+---------+----------+-----------+
| 1 | blah | date | 10.00 | 0.00 |
| 2 | blah | date | 2,027.10 | 27.10 |
| 2 | blah | date | 0.00 | 2000.00 |
| 1 | blah | date | 0.00 | 10.00 |
| 3 | blah | date | 5000.00 | 0.00 |
+-------------+------------------+---------+----------+-----------+
I would like to be able to type in a location ID and then have results show up (in this example I type 2 for the location)
+---------+----------+-----------+------+
| Date Of | Money In | Money Out | |
+---------+----------+-----------+------+
| date | 2027.10 | 27.10 | |
| date | 0 | 2000 | |
| Total: | 2027.10 | 2027.10 | 0 |
+---------+----------+-----------+------+
I have tried other solutions (One of which was pointed out below), however, they don't show the sum of each entire column, they simply subtract MoneyOUT from MoneyIN for each row. As of now, I am trying to do this in a query, but if there is a better way, please elaborate.
I am extremely new to SQL and Access, so please make the explanation understandable for a beginner like me. Thanks so much!
This is a table referred to below.
+-------------+-------+----------+-----------+-----------+
| Location ID | Date | Money IN | Money Out | Total Sum |
+-------------+-------+----------+-----------+-----------+
| 1 | date | 300 | 200 | |
| 1 | date | 300 | 200 | |
| 1 | date | 300 | 200 | |
| 1 | total | 900 | 600 | 300 |
+-------------+-------+----------+-----------+-----------+

The following should give you what you want:
SELECT DateOf, MoneyIn, MoneyOut, '' AS TotalSum FROM YourTable
UNION
SELECT 'Total', SUM(MoneyIn) AS SumIn, SUM(MoneyOut) AS SumOut,
SUM(MoneyIn - MoneyOut) AS TotalSum FROM YourTable
Edit:
You do not need to alter very much to achieve what you want. In order to get Access to prompt for a parameter when running a query, you give a name for the parameter in square brackets; Access will then pop-up a window prompting the user for this value. Also this parameter can be used more than once in the query, without Access prompting for it multiple times. So the following should work for you:
SELECT DateOf, MoneyIn, MoneyOut, '' AS TotalSum
FROM YourTable
WHERE LocationID=[Location ID]
UNION
SELECT 'Total', SUM(MoneyIn) AS SumIn, SUM(MoneyOut) AS SumOut,
SUM(MoneyIn - MoneyOut) AS TotalSum FROM YourTable
WHERE LocationID=[Location ID];
However, looking at your table design, I strongly encourage you to change it. You are including the address on every record. If you have three locations, but 100 records, then on average you are unnecessarily repeating each address more than 30 times. The "normal" way to avoid this would be to have a second table, Locations, which would have an ID and an Address field. You then remove address from YourTable, and in its place create a one-to-many relationship between the ID in Locations and the LocationID in YourTable.

It's a little unclear exactly what you expect without sample data, but I think this is what you want:
SELECT DateOf, SUM(MoneyIN) - SUM(MoneyOut)
FROM YourTable
GROUP BY DateOf
This will subtract the summed total of MoneyOut from MoneyIn at each distinct DateOf
Updated Answer
A UNION will let you append a 'Totals' record to the bottom of your result set:
SELECT *
FROM (
SELECT CAST(DateOf as varchar(20)) as DateOf, MoneyIn, MoneyOut, '' as NetMoneyIn
FROM YourTable
UNION
SELECT 'Total:', SUM(MoneyIn), SUM(MoneyOut), SUM(MoneyIN) - SUM(MoneyOut)
FROM YourTable
) A
ORDER BY CASE WHEN DateOf <> 'Total:' THEN 0 ELSE 1 END, DateOf
Some notes.. I used a derived table to ensure that the 'Total' record is last. Also casted DateOf to a string (assuming it is a date), otherwise you will have issues writing the string 'Total:' to that column.

Related

Query columns based on values of table

I'm new to DB and SQL so I don't know if there is anything new I need to try for this. I want to solve this table my senior has given to me:
Passbook(Table Name)
Date | Amount | Type
-----------------------------
14/3/19 | 48000 | Debit |
13/2/19 | 75000 | Credit|
9/7/19 | 65000 | Credit|
12/6/19 | 15000 | Debit |
Now I have to generate a query in this manner:
Month | Debit | Credit
------------------------------
13/2/19 | 0 | 75000
14/3/19 | 48000 | 0
12/6/19 | 15000 | 0
9/7/19 | 0 | 65000
Here my Passbook table value has become the columns for query and IDK how to generate it in this manner
Anyone help me do this please
for monthly sorting, I'm supposed to use ORDER BY clause, I suppose
Now I have to generate a query in that manner.
A basic pivot query should work here:
SELECT
Format(Month([Date])) AS Month,
SUM(IIF(Type = 'Debit', Amount, 0)) AS Debit,
SUM(IIF(Type = 'Credit', Amount, 0)) AS Credit
FROM yourTable
GROUP BY
Format(Month([Date]));
If you instead want date level output, then aggregate the by the Date column directly.

Remove row values and convert into single column value

I have a stored procedure returning a table that looks like this:
ID | Type | Price | Description
-------------------------------
7 | J | 50.00 | Job
7 | F | 20.00 | Freight
7 | P | 30.00 | Postage
7 | H | 5.00 | Handling
I would like it to return the table like this:
ID | Type | Price | Description | FreightPrice
-----------------------------------------
7 | J | 50.00 | Job | 20.00
7 | P | 30.00 | Postage | 20.00
7 | H | 5.00 | Handling | 20.00
Is there a way that I can use a query such as:
SELECT * FROM Temp WHERE Type = 'F'
but return the 'Freight' row as a column instead with just the 'Price' value?
From what I have seen it appears that I may need to use the PIVOT operator to achieve this but that seems overly complex. Is there a way that I could achieve this result using a CASE or IF expression?
Based on the data you provided, there is one row having the description value: "Freight". Assuming this is the case, then try:
select ID,Type,Price,Description,
FreightPrice = (select Price
from mytable
where Description = 'Freight')
from mytable
where Description <> 'Freight'
If the Freight row is always moved to the right you can hard code this logic (assuming it's always a single row), as in:
select
id,
type,
price,
description,
(select price from t where description = 'Freight') as freightprice
from t
where description <> 'Freight'
Note: this query will crash if your table has more than one row for Freight.

SQL deleting rows with duplicate dates conditional upon values in two columns

I have data on approx 1000 individuals, where each individual can have multiple rows, with multiple dates and where the columns indicate the program admitted to and a code number.
I need each row to contain a distinct date, so I need to delete the rows of duplicate dates from my table. Where there are multiple rows with the same date, I need to keep the row that has the lowest code number. In the case of more than one row having both the same date and the same lowest code, then I need to keep the row that also has been in program (prog) B. For example;
| ID | DATE | CODE | PROG|
--------------------------------
| 1 | 1996-08-16 | 24 | A |
| 1 | 1997-06-02 | 123 | A |
| 1 | 1997-06-02 | 123 | B |
| 1 | 1997-06-02 | 211 | B |
| 1 | 1997-08-19 | 67 | A |
| 1 | 1997-08-19 | 23 | A |
So my desired output would look like this;
| ID | DATE | CODE | PROG|
--------------------------------
| 1 | 1996-08-16 | 24 | A |
| 1 | 1997-06-02 | 123 | B |
| 1 | 1997-08-19 | 23 | A |
I'm struggling to come up with a solution to this, so any help greatly appreciated!
Microsoft SQL Server 2012 (X64)
The following works with your test data
SELECT ID, date, MIN(code), MAX(prog) FROM table
GROUP BY date
You can then use the results of this query to create a new table or populate a new table. Or to delete all records not returned by this query.
SQLFiddle http://sqlfiddle.com/#!9/0ebb5/5
You can use min() function: (See the details here)
select ID, DATE, min(CODE), max(PROG)
from table
group by DATE
I assume that your table has a valid primary key. However i would recommend you to take IDas Primary key. Hope this would help you.

SQL server - advanced grouping

I have at table containing procurement contracts that looks like this:
+------+-----------+------------+---------+------------+-----------+
| type | text | date | company | supplierID | name |
+ -----+-----------+------------+---------+------------+-----------+
| 0 | None | 2004-03-29 | 310 | 227234 | HRC INFRA |
| 0 | None | 2007-09-30 | 310 | 227234 | HRC INFRA |
| 0 | None | 2010-11-29 | 310 | 227234 | HRC INFRA |
| 2 | Strategic | 2011-01-01 | 310 | 227234 | HRC INFRA |
| 0 | None | 2012-04-10 | 310 | 227234 | HRC INFRA |
+------+-----------+------------+---------+------------+-----------+
In this example the first three rows the contract is the same. So I only want the first one.
The row with type = 2 is a change in procurement contract with the given supplier. I want to select that row as well.
On the last row the contract changes back to 0, so I want to select that row as well.
Basically I want to select the first row and the rows where the contract type changes. So the result should look like this:
+------+-----------+------------+---------+------------+-----------+
| type | text | date | company | supplierID | name |
+ -----+-----------+------------+---------+------------+-----------+
| 0 | None | 2004-03-29 | 310 | 227234 | HRC INFRA |
| 2 | Strategic | 2011-01-01 | 310 | 227234 | HRC INFRA |
| 0 | None | 2012-04-10 | 310 | 227234 | HRC INFRA |
+------+-----------+------------+---------+------------+-----------+
Any suggestions to how I can accomplish this?
;WITH cte AS
(
SELECT ROW_NUMBER() OVER (ORDER BY date) AS Id,
type, text, date, company, supplierId, name
FROM your_table
)
SELECT c1.type, c1.text, c1.date, c1.company,
c1.supplierId, c1.name
FROM cte c1 LEFT JOIN cte c2 ON c1.id = c2.id + 1
WHERE c2.text IS NULL OR c1.text != c2.text
Demo on SQLFiddle
I don't have SQL server in front of me to test it out so I'm not going to attempt the actual solution for it right now, but fyi there are few things you need:
1) A way to make sure the records are ordered properly. I don't see any kind of an id here which means you have no guarantee that they will appear in that order. I assume there is one so just make sure you order by it
2) You need to do an outer join on the table to itself on whatever the index is, but instead of "table1.index = table2.index" it will look like "table1.index = table2.index + 1". If your indexes aren't sequential then it will make joining them this way more complex than that though.
3) In the where clause you'll specify something like
where table1.type <> table2.type
That will get you most the way there. That won't pick up the very first record though since there is no record before the first record to compare to so you'll need an OR addition to compensate for that. And I'm assuming that type has no NULL values.
Sorry I couldn't be more help with an actual implementation but maybe someone else will take care of that shortly.
might be what you want. Presumingly you dont have type < 0.
SELECT *
FROM [TABLE] as ot where ot.type <>
(select top 1 coalesce(it.type, -1) from [TABLE] as it where it.date < ot.date order by it.date desc)
Also, take not of brandon note to make shure tables are ordered, due i dont see PK.

Extract data from largest date

I am having recods as below
---------------------------------------------------------------------
| AcnttNo | Date1 | Balance1 | Date2 | balance3 | date4 | balance4 |
|--------------------------------------------------------------------
| 123 | 50282 | 3456 | 45465 | 56557 | 4556 | 324235 |
| 123 | 56757 | 23434 | 234235 | 344324 | 56476 | 5676 |
| 123 | 435 | 2434 | 2343 | 234545 | 24245 | 2423424 |
---------------------------------------------------------------------
For example:
for each AcnttNo there will be several rows of data for balance and date.
I need to get the balance for largest date.
I'm using PL/SQL developer and an oracle database
If you want the row with the greatest date:
select
*
from
YourTable y
where
greatest(y.date1, y.date2, y.date3) =
(select max(greatest(yx.date1, yx.date2, yx.date3))
from
YourTable yx)
If you do actually need the balance matching the greatest date on that row:
select
greatest(y.date1, y.date2, y.date3) as GreatestDate,
case greatest(y.date1, y.date2, y.date3)
when y.Date1 then
y.balance1
when y.date2 then
y.balance2
when y.date3 then
y.balance3
end as GreatestDateBalance
from
YourTable y
where
greatest(y.date1, y.date2, y.date3) =
(select max(greatest(yx.date1, yx.date2, yx.date3))
from
YourTable yx)
But I think what you really need, is to reconsider your table design. :)
I'm not sure why you've multiple dates / balances in your table, however, the below should get you something interesting that you can work on...
SELECT *
FROM YourTable T
WHERE NOT EXISTS (
SELECT *
FROM YourTable T2
WHERE T2.AcntNo = T.AcntNo
AND T2.Date1 > T.Date1
)