How to calculate Total transactions amount column with input / output type? - sql

I have two tables : (transactions and Accounts) their columns are as shown in the picture bellow:
the Accounts table is simple just two columns ID and account name.
The important columns in the Transactions table are Amount and type, the type indicates whether the transaction is an input to the account or an output.
I want to find the current total amount (input - output) for each account with SQL.
this what I have tried but I couldn't go further:
select c.TRS_AC_ID, CompAccount, sum(Amount), Type from Accounts c
INNER JOIN Transactions t on c.TRS_AC_ID = t.TRS_AC_ID GROUP by CompAccount, Type

Instead of grouping by the type, you could use a case expression to return "input" transactions as is and "output" transactions as negative numbers:
SELECT c.TRS_AC_ID, CompAccount, SUM(CASE type WHEN 'O' THEN -1 * amount ELSE amount END)
FROM Accounts c
JOIN Transactions t on c.TRS_AC_ID = t.TRS_AC_ID
GROUP BY c.TRS_AC_ID, CompAccount

you can use the following SQL to get the result of input-output, however, if you need individual results then you can use either sub-query as well.
select
(select sum(amount) as input from transaction
where trs_ac_id = '1' and type_io = 'I'))
-
(seelect sum(amount) as output from transaction
where trs_ac_id = '2' and type_io = 'O')) as current_tot_amt
from dual;

Related

Select only the row with the max value, but the column with this info is a SUM()

I have the following query:
SELECT DISTINCT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT
FROM TGFCAB CAB, TGFPAR PAR, TSIBAI BAI
WHERE CAB.CODPARC = PAR.CODPARC
AND PAR.CODBAI = BAI.CODBAI
AND CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI
Which the result is this. Company names and neighborhood hid for obvious reasons
The query at the moment, for those who don't understand Latin languages, is giving me clients, company name, company neighborhood, and the total value of movements.
in the WHERE clause it is only filtering sales movements of companies from an established city.
But if you notice in the Select statement, the column that is retuning the value that aggregates the total amount of value of sales is a SUM().
My goal is to return only the company that have the maximum value of this column, if its a tie, display both of em.
This is where i'm struggling, cause i can't seem to find a simple solution. I tried to use
WHERE AMOUNT = MAX(AMOUNT)
But as expected it didn't work
You tagged the question with the whole bunch of different databases; do you really use all of them?
Because, "PL/SQL" reads as "Oracle". If that's so, here's one option.
with temp as
-- this is your current query
(select columns,
sum(vrlnota) as amount
from ...
where ...
)
-- query that returns what you asked for
select *
from temp t
where t.amount = (select max(a.amount)
from temp a
);
You should be able to achieve the same without the need for a subquery using window over() function,
WITH T AS (
SELECT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT,
MAX(VLRNOTA) over() AS MAMOUNT
FROM TGFCAB CAB
JOIN TGFPAR PAR ON PAR.CODPARC = CAB.CODPARC
JOIN TSIBAI BAI ON BAI.CODBAI = PAR.CODBAI
WHERE CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY CAB.CODPARC, PAR.RAZAOSOCIAL, BAI.NOMEBAI
)
SELECT CODPARC, RAZAOSOCIAL, NOMEBAI, AMOUNT
FROM T
WHERE AMOUNT=MAMOUNT
Note it's usually (always) beneficial to join tables using clear explicit join syntax. This should be fine cross-platform between Oracle & SQL Server.

How to to get two columns of data unrelated to each other in one sql query statement?

I need to get a state level count on number of services. For the purposes of this I only have two services. The first column is the states, the second column is the first services and the third column is the second service. What I am struggling with is to have the second and third column show up on the results in one query. Here is my code:
SELECT Distinct allstates.Name, count (data.StateName) as CareCase_Management_Services, count(data.StateName) Caregiver_Support_Services
From
(select distinct Name from USstate) allstates
Left Join
Client2017 data
on
allstates.Name = data.StateName and
data.FiscalYear = 2017 and
data.SrvstartCareCaseMgmtCode NOT IN('999','', '998') and
data.SrvstartCaregiverSuppCode NOT IN('999','', '998')
GROUP BY allstates.Name
ORDER BY allstates.Name ASC
I understand that you are looking to compute, for each state, the count of services that match certain criteria. There are two types of services, stored in two different columns.
If so, your query could be simplified using conditional aggregation :
SELECT
allstates.Name,
SUM(CASE WHEN c.SrvstartCareCaseMgmtCode NOT IN ('999', '', '998') THEN 1 ELSE 0 END) CareCase_Management_Services,
SUM(CASE WHEN c.SrvstartCaregiverSuppCode NOT IN ('999', '', '998') THEN 1 ELSE 0 END) Caregiver_Support_Services
FROM
(SELECT DISTINCT Name FROM USstate) s
LEFT JOIN Client2017 c ON s.Name = c.StateName AND c.FiscalYear = 2017
GROUP BY allstates.Name
With this technique, each service is counted according to its own logic ; when conditions are met, the record is counted in (1 is added to the SUM()), else it is ignored (+ 0).
NB : do you really have duplicated state names in USstate ? if no, you can replace subquery (SELECT DISTINCT Name FROM USstate) s with just USstate

Calculated column syntax when using a group by function Teradata

I'm trying to include a column calculated as a % of OTYPE.
IE
Order type | Status | volume of orders at each status | % of all orders at this status
SELECT
T.OTYPE,
STATUS_CD,
COUNT(STATUS_CD) AS STATVOL,
(STATVOL / COUNT(ROW_ID)) * 100
FROM Database.S_ORDER O
LEFT JOIN /* Finding definitions for status codes & attaching */
(
SELECT
ROW_ID AS TYPEJOIN,
"NAME" AS OTYPE
FROM database.S_ORDER_TYPE
) T
ON T.TYPEJOIN = ORDER_TYPE_ID
GROUP BY (T.OTYPE, STATUS_CD)
/*Excludes pending and pending online orders */
WHERE CAST(CREATED AS DATE) = '2018/09/21' AND STATUS_CD <> 'Pending'
AND STATUS_CD <> 'Pending-Online'
ORDER BY T.OTYPE, STATUS_CD DESC
OTYPE STATUS_CD STATVOL TOTALPERC
Add New Service Provisioning 2,740 100
Add New Service In-transit 13 100
Add New Service Error - Provisioning 568 100
Add New Service Error - Integration 1 100
Add New Service Complete 14,387 100
Current output just puts 100 at every line, need it to be a % of total orders
Could anyone help out a Teradata & SQL student?
The complication making this difficult is my understanding of the group by and count syntax is tenuous. It took some fiddling to get it displayed as I have it, I'm not sure how to introduce a calculated column within this combo.
Thanks in advance
There are a couple of places the total could be done, but this is the way I would do it. I also cleaned up your other sub query which was not required, and changed the date to a non-ambiguous format (change it back if it cases an issue in Teradata)
SELECT
T."NAME" as OTYPE,
STATUS_CD,
COUNT(STATUS_CD) AS STATVOL,
COUNT(STATUS_CD)*100/TotalVol as Pct
FROM database.S_ORDER O
LEFT JOIN EDWPRDR_VW40_SBLCPY.S_ORDER_TYPE T on T.ROW_ID = ORDER_TYPE_ID
cross join (select count(*) as TotalVol from database.S_ORDER) Tot
GROUP BY T."NAME", STATUS_CD, TotalVol
WHERE CAST(CREATED AS DATE) = '2018-09-21' AND STATUS_CD <> 'Pending' AND STATUS_CD <> 'Pending-Online'
ORDER BY T."NAME", STATUS_CD DESC
A where clause comes before a group by clause, so the query
shown in the question isn't valid.
Always prefix every column reference with the relevant table alias, below I have assumed that where you did not use the alias that it belongs to the orders table.
You probably do not need a subquery for this left join. While there are times when a subquery is needed or good for performance, this does not appear to be the case here.
Most modern SQL compliant databases provide "window functions", and Teradata does do this. They are extremely useful, and here when you combine count() with an over clause you can get the total of all rows without needing another subquery or join.
Because there is neither sample data nor expected result provided with the question I do not actually know which numbers you really need for your percentage calculation. Instead I have opted to show you different ways to count so that you can choose the right ones. I suspect you are getting 100 for each row because the count(status_cd) is equal to the count(row_id). You need to count status_cd differently to how you count row_id. nb: The count() function increases by 1 for every non-null value
I changed the way your date filter is applied. It is not efficient to change data on every row to suit constants in a where clause. Leave the data untouched and alter the way you apply the filter to suit the data, this is almost always more efficient (search sargable)
SELECT
t.OTYPE
, o.STATUS_CD
, COUNT(o.STATUS_CD) count_status
, COUNT(t.ROW_ID count_row_id
, count(t.row_id) over() count_row_id_over
FROM dbo.S_ORDER o
LEFT JOIN dbo.S_ORDER_TYPE t ON t.TYPEJOIN = o.ORDER_TYPE_ID
/*Excludes pending and pending online orders */
WHERE o.CREATED >= '2018-09-21' AND o.CREATED < '2018-09-22'
AND o.STATUS_CD <> 'Pending'
AND o.STATUS_CD <> 'Pending-Online'
GROUP BY
t.OTYPE
, o.STATUS_CD
ORDER BY
t.OTYPE
, o.STATUS_CD DESC
As #TomC already noted, there's no need for the join to a Derived Table. The simplest way to get the percentage is based on a Group Sum. I also changed the date to an Standard SQL Date Literal and moved the where before group by.
SELECT
t."NAME",
o.STATUS_CD,
Count(o.STATUS_CD) AS STATVOL,
-- rule of thumb: multiply first then divide, otherwise you will get unexpected results
-- (Teradata rounds after each calculation)
100.00 * STATVOL / Sum(STATVOL) Over ()
FROM database.S_ORDER AS O
/* Finding definitions for status codes & attaching */
LEFT JOIN database.S_ORDER_TYPE AS t
ON t.ROW_ID = o.ORDER_TYPE_ID
/*Excludes pending and pending online orders */
-- if o.CREATED is a Timestamp there's no need to apply the CAST
WHERE Cast(o.CREATED AS DATE) = DATE '2018-09-21'
AND o.STATUS_CD NOT IN ('Pending', 'Pending-Online')
GROUP BY (T.OTYPE, o.STATUS_CD)
ORDER BY T.OTYPE, o.STATUS_CD DESC
Btw, you probably don't need an Outer Join, Inner should return the same result.

How to use SUM in this situation?

I have the following tables below and their schema:
INV
id, product code, name, ucost, tcost, desc, type, qoh
1,123,CPASS 700,1.00,5.00,CPASS 700 Lorem, COM,5
2,456,Shelf 5,2.00,6.00,Shelf 5 KJ, BR,3
GRP
id,type,desc
1,COM,COMPASS
2,BR,SHELF
Currently I have a query like this:
SELECT INV.*,GRP.DESCR AS CATEGORY
FROM INV LEFT JOIN GRP ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0
There is no problems with that query.
Right now,I want to know the SUM of the TCOST of every INV record where their QOH is 0.
In this situation, does that I mean all I have to do is to write a separate query like the one below:
SELECT SUM(TCOST)
FROM INV
WHERE QOH = 0
Does it make any sense for me to try and combine those two queries as one ?
First understand that SUM is the aggregate function hence either you can run the Query like
(SELECT SUM(TCOST) FROM INV WHERE QOH=0) as total
This will return Sum of TCOST in INV Table for mentioned condition.
Another approach is finding the Sum based on the some column (e.g. Type)
you could write query like
SELECT Type , SUM(TCOST) FROM INV WHERE QOH=0 GROUP BY type ;
Its not clear on what criteria you want to sum . But I think above two approaches would provide you fare idea .
Mmm, you could maybe use a correlated query, though i'm not sure it's the best approach since I'm not sure I understand what your attempting to do:
SELECT INV.*,
GRP.DESCR AS CATEGORY ,
(SELECT SUM(TCOST) FROM INV WHERE QOH=0) as your_sum
FROM INV LEFT JOIN GRP ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0
If you want only one value for the sum(), then your query is fine. If you want a new column with the sum, then use window functions:
SELECT INV.*, GRP.DESCR AS CATEGORY,
SUM(INV.TCOST) OVER () as sum_at_zero
FROM INV LEFT JOIN
GRP
ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0;
It does not make sense to combine the queries by adding a row to the first one, because the columns are very different. A SQL result set requires that all rows have the same columns.

How to using subquery values in arithmetic operations

My manager wants to see what is total values of our suppliers shipments, and what is the total values of their invoices recorded. So he can see the differencies and want from suppliers to unsended invoices.
Here is my code.
It is working on accounting table and shipment detail table.
fis_meblag0 is always little from zero because it is 320 account so I mutiply it -1 for get real value.
sth_tutar is value of shipment, sth_vergi is VAT and so the sum of them is equal to sum of total with VAT.
Now the hard part.
Manager wants to diference between them in a other column and also sort the valuez z to a.
I know I can reuse same subselect for the getting totals but I want to know that can I achieve this without using the same subquery again.
I mean in first subselect I have the total, in last column I only need just the total, can I use total without compute it again?
Regards
select
ch.cari_kod as Carikod,
ch.cari_unvan1 as Unvani,
(select (sum(mf.fis_meblag0) * -1)
from dbo.MUHASEBE_FISLERI mf
where (mf.fis_tarih > '20141231' and mf.fis_tarih < '20150201')
and mf.fis_hesap_kod = ch.cari_kod
and mf.fis_meblag0 < 0) as mtoplam,
(Select sum (sth.sth_tutar + sth.sth_vergi)
from dbo.STOK_HAREKETLERI sth
where (sth.sth_tarih > '20141231' and sth.sth_tarih < '20150201')
and sth.sth_cari_kodu = ch.cari_kod
and sth.sth_normal_iade = 0
and sth.sth_tip = 0) as stoplam
from
dbo.CARI_HESAPLAR ch
where
ch.cari_kod like '320%'
try this query:
select Carikod, Unvani, mtoplam, stoplam, mtoplam - stoplam as Grand_total
from
(
-- your full query here
) T