I have data in the following format in a table:
Acct# Amount
123 3.4
123T 4.5
124 2.3
124T 4.5
125 1.2
125T 2.4
How do I create a select statement where it totals up the account number as 123+123T and gives the following output:
123 7.9
124 6.8
125 3.6
You don't say any particular dialect of SQL
SELECT LEFT(Acct#,3), SUM(Amount)
FROM yourTable
GROUP BY LEFT(Acct#,3)
Or to handle arbitrary length account numbers
SELECT
CASE
WHEN Acct# LIKE '%T'
THEN SUBSTRING(Acct#,1,LEN(#Acct)-1)
ELSE Acct#
END,
SUM(Amount)
FROM yourTable
GROUP BY
CASE
WHEN Acct# LIKE '%T'
THEN SUBSTRING(Acct#,1,LEN(#Acct)-1)
ELSE Acct#
END
Or a more generic approach that will handle arbitrary mappings might be to construct a mapping table that you can then join on. There is quite a lot of missing information here as to the rules that need to be applied!
SELECT d.b, SUM(yt.Amount)
FROM yourTable yt
join (
SELECT '123' as a, '123' as b UNION ALL
SELECT '123T' as a, '123' as b UNION ALL
SELECT '124' as a, '124' as b UNION ALL
SELECT '124T' as a, '124' as b UNION ALL
SELECT '125' as a, '125' as b UNION ALL
SELECT '125T' as a, '125' as b
) d ON d.a = yt.Acct#
GROUP BY d.b
You can also try
SELECT REPLACE([Acct#],'T', ''), SUM(Amount)
FROM Acct
GROUP BY REPLACE([Acct#],'T', '')
Test Data
create table acct
([acct#] varchar(10),
amount decimal(10,2)
)
insert into acct([acct#], amount) values ('123', 3.4 )
insert into acct([acct#], amount) values ('123T', 4.5 )
insert into acct([acct#], amount) values ('124', 2.3)
insert into acct([acct#], amount) values ('124T', 4.5)
insert into acct([acct#], amount) values ('125', 1.2)
insert into acct([acct#], amount) values ('125T', 2.4)
I would have done that:
select b.acct#, (a.Amount + b.Amount) as Amount
FROM yourTable as a inner join yourTable as b
ON a.acct# = b.acct# + 'T'
Related
Table:
table
My goal is to find the code value in the row, that has the highest absolute value in the amt column while cosidering that I am only searching for max absolute between those rows, that have the same pair of id and id_addl.
For example - the first 3 rows have matching id + id_addl combinations, so I find the highest absolute (this case it's 43562). Now I need to find that row's code, which would be CLP.
Here is what I have:
with max_code as(
select
s.id,
s.id_addl,
s.amt,
s.code
from
schema.viw s
)
select
b.code,
b.amt,
b.id,
b.id_addl,
b.max_amt,
m.code
from
(
select
a.code,
a.amt,
a.id,
a.id_addl,
max(abs(a.amt))over(partition by a.id, a.id_addl) max_amt
from
schema.viw a
) b,
max_code m
where
b.id = m.id and
b.id_addl = m.id_addl and
b.max_amt = m.amt;
Any idea how I can improve it?
Use MAX(...) KEEP (...) OVER (...):
SELECT code,
amt,
id,
id_addl,
MAX(ABS(amt))
OVER (PARTITION BY id, id_addl) AS max_amt,
MAX(code)
KEEP (DENSE_RANK LAST ORDER BY ABS(amt))
OVER (PARTITION BY id, id_addl)
AS max_code
FROM schema.viw;
Which, for the sample data:
CREATE TABLE schema.viw (id, id_addl, amt, code) AS
SELECT 1, 4132, 23432, 'USD' FROM DUAL UNION ALL
SELECT 1, 4132, 12354, 'EUR' FROM DUAL UNION ALL
SELECT 1, 4132, 43562, 'CLP' FROM DUAL UNION ALL
SELECT 2, 3124, 84563, 'EUR' FROM DUAL;
Outputs:
CODE
AMT
ID
ID_ADDL
MAX_AMT
MAX_CODE
USD
23432
1
4132
43562
CLP
EUR
12354
1
4132
43562
CLP
CLP
43562
1
4132
43562
CLP
EUR
84563
2
3124
84563
EUR
db<>fiddle here
MS SQL Server
I have two tables with different accounts from the same customer:
Table1:
ID
ACCOUNT
FROM
TO
1
A
01.10.2019
01.12.2019
1
A
01.02.2020
09.09.9999
and table2:
ID
ACCOUNT
FROM
TO
1
B
01.12.2019
01.01.2020
As result I want a table that summarize the story of this costumer and shows when he had an active account and when he doesn't.
Result:
ID
FROM
TO
ACTIV Y/N
1
01.10.2019
01.01.2020
Y
1
02.01.2020
31.01.2020
N
1
01.02.2020
09.09.9999
Y
Can someone help me with some ideas how to proceed?
This is the typical gaps and island problem, and it's not usually easy to solve.
You can achieve your goal using this query, I will explain it a little bit.
You can test on this db<>fiddle.
First of all... I have unified your two tables into one to simplify the query.
-- ##table1
select 1 as ID, 'A' as ACCOUNT, convert(date,'2019-10-01') as F, convert(date,'2019-12-01') as T into ##table1
union all
select 1 as ID, 'A' as ACCOUNT, convert(date,'2020-02-01') as F, convert(date,'9999-09-09') as T
-- ##table2
select 1 as ID, 'B' as ACCOUNT, convert(date,'2019-12-01') as F, convert(date,'2020-01-01') as T into ##table2
-- ##table3
select * into ##table3 from ##table1 union all select * from ##table2
You can then get your gaps and island using, for example, a query like this.
It combines recursive cte to generate a calendar (cte_cal) and lag and lead operations to get the previous/next record information to build the gaps.
with
cte_cal as (
select min(F) as D from ##table3
union all
select dateadd(day,1,D) from cte_cal where d < = '2021-01-01'
),
table4 as (
select t1.ID, t1.ACCOUNT, t1.F, isnull(t2.T, t1.T) as T, lag(t2.F, 1,null) over (order by t1.F) as SUP
from ##table3 t1
left join ##table3 t2
on t1.T=t2.F
)
select
ID,
case when T = D then F else D end as "FROM",
isnull(dateadd(day,-1,lead(D,1,null) over (order by D)),'9999-09-09') as "TO",
case when case when T = D then F else D end = F then 'Y' else 'N' end as "ACTIV Y/N"
from (
select *
from cte_cal c
cross apply (
select t.*
from table4 t
where t.SUP is null
and (
c.D = t or
c.D = dateadd(day,1,t.T)
)
) t
union all
select F, * from table4 where T = '9999-09-09'
) p
order by 1
option (maxrecursion 0)
Dates like '9999-09-09' must be treated like exceptions, otherwise I would have to create a calendar until that date, so the query would take long time to resolve.
I have two tables: rate_card and rate configured as follows:
create table rate_card (RateCard varchar(13), Currency varchar(3));
insert into rate_card values('2DaysUSD','USD');
insert into rate_card values('3DaysUSD','USD');
insert into rate_card values('2DaysJPY','JPY');`table`
create table rate (Currency varchar(3),Rate varchar(19));
insert into rate values ('USD','0.6');
insert into rate values('JPY','0.4');
I want to get the output as below:
Yellow once are to be shown when I run the query as Ratecard = '1DaysUSD' is not available.
For others I simply used Union to display three Records 'Spot', 'Today', 'Tomorrow' as below:
select c.RateCard, c.Currency, 'SPOT' from rate_card c, rate r where c.Currency = r.Currency
union
select c.RateCard, c.Currency, 'Today' from rate_card c, rate r where c.Currency = r.Currency
union
select c.RateCard, c.Currency, 'Tomorrow' from rate_card c, rate r where c.Currency = r.Currency;
Could you please advise me on the right approach?
You can achieve this using multiple subquery and connect by as following:
Select RateCard,
Currency,
rate,
Case lvl when 1 then 'Spot'
When 2 then 'Today'
Else 'Tomorrow'
end as type
From
(select c.RateCard,
c.Currency,
r.rate
from (Select ratecard,
currency
from rate_card
Union
Select &&your_inpur_rate_card,
substr(&&your_inpur_rate_card, length(&&your_inpur_rate_card - 2))
from dual) c
join rate r
on c.Currency = r.Currency)
Join
(Select level as lvl
from dual
connect by level <= 3)
on 1=1;
Cheers!!
I have three columns
suppose
row no column1 column2 column3
1 A B C
2 A B C
3 D E F
4 G H I
5 G H C
I want to generate code by combining these three column values
For Eg.
1)ABC001
2)ABC002
3)DEF001
4)GHI001
5)GHC001
by checking combination of three columns
logic is that
if values of three columns are same then like first time it shows 'ABC001'
and 2nd time it shows 'ABC002'
You can try this:
I dont know what you want for logic with 00, but you can add them manuel or let the rn decide for you
declare #mytable table (rowno int,col1 nvarchar(50),col2 nvarchar(50),col3 nvarchar(50)
)
insert into #mytable
values
(1,'A', 'B', 'C'),
(2,'A', 'B', 'C'),
(3,'D', 'E', 'F'),
(4,'G', 'H', 'I'),
(5,'G', 'H', 'C')
Select rowno,col1,col2,col3,
case when rn >= 10 and rn < 100 then concatcol+'0'+cast(rn as nvarchar(50))
when rn >= 100 then concatcol+cast(rn as nvarchar(50))
else concatcol+'00'+cast(rn as nvarchar(50)) end as ConcatCol from (
select rowno,col1,col2,col3
,Col1+col2+col3 as ConcatCol,ROW_NUMBER() over(partition by col1,col2,col3 order by rowno) as rn from #mytable
) x
order by rowno
My case when makes sure when you hit number 10 it writes ABC010 and when it hits above 100 it writes ABC100 else if its under 10 it writes ABC001 and so on.
Result
TSQL: CONCAT(column1,column2,column3,RIGHT(REPLICATE("0", 3) + LEFT(row_no, 3), 3))
You should combine your columns like below :
SELECT CONVERT(VARCHAR(MAX), ROW_NUMBER() OVER(ORDER BY
(
SELECT NULL
)))+') '+DATA AS Data
FROM
(
SELECT column1+column2+column3+'00'+CONVERT(VARCHAR(MAX), ROW_NUMBER() OVER(PARTITION BY column1,
column2,
column3 ORDER BY
(
SELECT NULL
))) DATA
FROM <table_name>
) T;
Result :
1)ABC001
2)ABC002
3)DEF001
4)GHI001
5)GHC001
MySQL:
CONCAT(column1,column2,column3,LPAD(row_no, 3, '0'))
[you will need to enclose the 'row no' in ticks if there is a space in the name of the field instead of underscore.]
I have 3 queries and they work fine. There Queries are:
SELECT SUM(SALES)as NETSALES FROM Sales WHERE DOCREF='1'GROUP BY GEOCODE
above query results :
NETSALES
1
2
3
SELECT SUM(SALES)as FRESHRETURNS FROM Sales WHERE DOCREF='2'GROUP BY GEOCODE
above query results :
FRESHRETURNS
1
2
3
SELECT SUM(SALES)as SALESRETURNS FROM Sales WHERE DOCREF='3'GROUP BY GEOCODE
above query results :
SALESRETURNS
1
2
3
is there any way to combine these statements to get the result as
NETSALES | FRESHRETURNS | SALESRETURNS
1------1-------|-----------1-----------|--------1--------
2------2-------|-----------2-----------|--------2--------
3------3-------|-----------2-----------|--------3--------
You didn't mention if you are using MS Sql Server or Oracle, I am assuming MS :)
Make use of CASE and you can basically build a matrix with the result you want:
CREATE TABLE #t
(
Sale int,
DocRef varchar(1),
GeoCode varchar(1)
)
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(100, '1', 'A')
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(120, '1', 'A')
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(110, '2', 'B')
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(120, '2', 'B')
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(100, '3', 'C')
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(100, '3', 'C')
INSERT INTO #t(Sale, DocRef,GeoCode) VALUES(100, '3', 'A')
SELECT
CASE WHEN DocRef='1' THEN SUM(Sale) ELSE 0 END as NETSALES,
CASE WHEN DocRef='2' THEN SUM(Sale) ELSE 0 END AS FRESHRETURNS,
CASE WHEN DocRef='3' THEN SUM(Sale) ELSE 0 END AS SALESRETURNS
FROM
#t
GROUP BY
GeoCode,
DocRef
DROP TABLE #t
I think this works but I haven't tested. I'm adding a fake ID column, giving it value 'x' and joining the 3 result sets using this new ID:
select t.NETSALES, ta.FRESHRETURNS , tb.SALESRETURNS from
(SELECT 'x' as ID, SUM(SALES)as NETSALES FROM Sales WHERE DOCREF='1'GROUP BY GEOCODE,ID ) as t
inner join
(SELECT 'x' as ID, SUM(SALES)as FRESHRETURNS FROM Sales WHERE DOCREF='2'GROUP BY GEOCODE,ID) ta on ta.ID=t.ID
inner join
(SELECT 'x' as ID SUM(SALES)as SALESRETURNS FROM Sales WHERE DOCREF='3'GROUP BY GEOCODE,ID ) tb on tb.ID=t.ID
Probably terribly inefficient but works for me on Oracle
SELECT (SELECT SUM(SALES) FROM Sales WHERE DOCREF='1'GROUP BY GEOCODE) NETSALES,
(SELECT SUM(SALES) FROM Sales WHERE DOCREF='2'GROUP BY GEOCODE) FRESHRETURNS ,
(SELECT SUM(SALES) FROM Sales WHERE DOCREF='3'GROUP BY GEOCODE) SALESRETURNS FROM DUAL
Depending on which database product you are using, you may need to tweak this a bit, but something like this should work for you:
SELECT GEOCODE, SUM(NETSALES), SUM(FRESHRETURNS), SUM(SALESRETURNS)
FROM
(
SELECT GEOCODE, SUM(SALES)as NETSALES, 0 AS FRESHRETURNS, 0 AS SALESRETURNS FROM Sales WHERE DOCREF='1'GROUP BY GEOCODE
UNION ALL
SELECT GEOCODE, 0 AS NETSALES, SUM(SALES)as FRESHRETURNS, 0 AS SALESRETURNS FROM Sales WHERE DOCREF='2'GROUP BY GEOCODE
UNION ALL
SELECT GEOCODE, 0 AS NETSALES, 0 AS FRESHRETURNS, SUM(SALES)as SALESRETURNS FROM Sales WHERE DOCREF='3'GROUP BY GEOCODE
) AS salesData
GROUP BY GEOCODE