How do I use flatten logic in Snowflake - snowflake-cloud-data-platform

select * from
(SELECT ID,purchase_list,
CASE WHEN purchase_list LIKE '12432%' THEN replace(purchase_list,12432,'Amt_Saved: Mastercard(12432)')
END AS purchase_amt
FROM (
SELECT ID,index,d.value::string AS purchase_list
FROM (
SELECT ID,c.value::string AS purchase_list
FROM table_1,LATERAL flatten(INPUT=>split(purchase_order_list, '|')) c
), LATERAL flatten(INPUT=>split(purchase_list, ';')) d
)
)WHERE purchase_amt is not null
When I run the above query, I'm getting results as mentioned below:
ID PURCHASE_LIST PURCHASE_AMT
12810789 12432=3.00 Savings1: Mastercard(12432)=3.00
12810789 12432=0.99 Savings1: Mastercard(12432)=0.99
12810789 12432=0.49 Savings1: Mastercard(12432)=0.49
I want to sum up these amounts (3.00 + 0.99 + 0.49)=4.48 and display this result in same column or another COLUMN
The result should look like:
ID PURCHASE_LIST PURCHASE_AMT
12810789 12432=4.48 4.48
How to implement this using SUM function by using any other logic?
Kindly guide. Thanks in advance :)

Firstly your existing SQL can be rewritten to be:
SELECT
a.id,
d.value::string AS purchase_list,
IFF( purchase_list LIKE '12432%', REPLACE(purchase_list, 12432, 'Amt_Saved: Mastercard(12432)'), null) purchase_amt
FROM table_1 AS a
,LATERAL flatten(INPUT=>split(a.purchase_order_list, '|')) c
,LATERAL flatten(INPUT=>split(c.value::string, ';')) d
WHERE purchase_amt IS NOT NULL;
So we can add a SUM() OVER() clause to get a total per row, it you are want parts and total:
SELECT
a.id,
d.value::string AS purchase_list,
IFF( purchase_list LIKE '12432%', REPLACE(purchase_list, 12432, 'Amt_Saved: Mastercard(12432)'), null) purchase_amt,
SUM(REGEXP_REPLACE(purchase_list, '12432=([0-9.]+)', '\\1', 1,1,'e')::number(9,3)) over (partition by id) as sum_total
FROM table_1 AS a
,LATERAL flatten(INPUT=>split(a.purchase_order_list, '|')) c
,LATERAL flatten(INPUT=>split(c.value::string, ';')) d
WHERE purchase_amt IS NOT NULL;
ID
PURCHASE_LIST
PURCHASE_AMT
SUM_TOTAL
12810789
12432=3.00
Amt_Saved: Mastercard(12432)=3.00
4.48
12810789
12432=0.99
Amt_Saved: Mastercard(12432)=0.99
4.48
12810789
12432=0.49
Amt_Saved: Mastercard(12432)=0.49
4.48
OR you can group and not care about all the sub values:
SELECT
a.id,
SUM(REGEXP_REPLACE(d.value::string, '12432=([0-9.]+)', '\\1', 1,1,'e')::number(9,3)) AS sum_total
FROM table_1 AS a
,LATERAL flatten(INPUT=>split(a.purchase_order_list, '|')) c
,LATERAL flatten(INPUT=>split(c.value::string, ';')) d
GROUP BY 1;
gives:
ID
SUM_TOTAL
12810789
4.48

Related

Invalid Column name and The multi-part identifier could not be bound in WHERE clause

I am trying to get the code below to work. My particular issue is when I add "gh" and "rn" in the last WHERE clause I get back either "Invalid column name" or "The multi-part identifier could not be bound" in all my attempts. Here is where the query stands at the moment. Any thoughts?
SELECT T.customer_no,
ref_no,
cont_dt,
cont_amt,
cust_type,
rn,
gh
FROM (SELECT customer_no,
ref_no,
cont_dt,
cont_amt,
cont_type,
rank()
OVER(
partition BY customer_no
ORDER BY T_contribution.cont_amt DESC) AS gh,
row_number()
OVER(
partition BY customer_no
ORDER BY T_contribution.cont_amt DESC) AS rn
FROM T_contribution
WHERE cont_dt <= '2015-12-31') AS T
INNER JOIN (SELECT customer_no
FROM T_CONTRIBUTION
GROUP BY customer_no
HAVING count(cont_dt) > 1) grouped
ON T.customer_no = grouped.customer_no
INNER JOIN T_CUSTOMER
ON T.customer_no = T_CUSTOMER.customer_no
WHERE ( cust_type = '1'
OR cust_type = '3'
OR cust_type = '7' )
AND NOT EXISTS (SELECT 1
FROM T_contribution AS t2
WHERE T.customer_no = t2.customer_no
AND t2.gh <> 1
AND t2.rn <> 2)
ORDER BY T.customer_no
I don't understand exactly the logic of your query but it looks that you are using the wrong alias with the columns "gh" and "rn" that come both from the nested query aliased T.
Your last WHERE clause should be:
WHERE cust_type IN ('1', '3', '7' ) AND NOT EXISTS (
SELECT 1
FROM T_contribution AS t2
WHERE T.customer_no = t2.customer_no
AND T.gh <> 1
AND T.rn <> 2 ) ORDER BY T.customer_no
Try this:
WITH DataSource AS
(
select customer_no, ref_no, cont_dt, cont_amt, cont_type,
rank() over(partition by customer_no order by T_contribution.cont_amt desc) as gh,
row_number() over(partition by customer_no order by T_contribution.cont_amt desc) as rn
from T_contribution
where cont_dt <= '2015-12-31'
)
select T.customer_no, ref_no, cont_dt, cont_amt, cust_type, rn, gh
from DataSource T
inner join
(
SELECT customer_no
FROM T_CONTRIBUTION
GROUP BY customer_no
having count(cont_dt) > 1
) grouped
ON T.customer_no = grouped.customer_no
inner join T_CUSTOMER
on T.customer_no = T_CUSTOMER.customer_no
where (cust_type='1' or cust_type='3' or cust_type='7')
and not exists
(
select 1
from DataSource as t2
where T.customer_no = t2.customer_no
and t2.gh <> 1and t2.rn <> 2
)
order by T.customer_no
You are not allowed to use ranking functions like RANK and ROW_NUMBER directly in the WHERE clause. That's because they are calculated in the SELECT phase, which is executed in after the WHERE one. So, you just need to wrapped them in common table expression (for example) and then to refer them in the WHERE clause.

Generate combinations in SQL Server

I need to generate combinations from the string of numbers
3,4,5,6,7 digit combinations
for example from this string
01;05;06;03;02;10;11;
here 7 numbers are there. for 3 digit 35 combinations will be there and it should be in order of order numbers in the string.
like
01;05;06;|
01;05;03;|
01;05;02;|
01;05;10;|
01;05;11;|
01;06;03;|
01;06;02;|
01;06;10;|
01;06;11;|
01;03;02;|
01;03;10;|
01;03;11;|
01;02;10;|
01;02;11;|
01;10;11;|
05;06;03;|
05;06;02;|
05;06;10;|
05;06;11;|
05;03;02;|
05;03;10;|
05;03;11;|
05;02;10;|
05;02;11;|
05;10;11;|
06;03;02;|
06;03;10;|
06;03;11;|
06;02;10;|
06;02;11;|
06;10;11;|
03;02;10;|
03;02;11;|
03;10;11;|
02;10;11;|
You can do this with two inner joins after splitting the string.
rextester: http://rextester.com/JJGKI77804
String Splitter for the test:
/* Jeff Moden's http://www.sqlservercentral.com/articles/Tally+Table/72993/ */
create function dbo.DelimitedSplitN4K (#pString nvarchar(4000), #pDelimiter nchar(1))
returns table with schemabinding as
return
with e1(n) as (
select 1 union all select 1 union all select 1 union all
select 1 union all select 1 union all select 1 union all
select 1 union all select 1 union all select 1 union all select 1
)
, e2(n) as (select 1 from e1 a, e1 b)
, e4(n) as (select 1 from e2 a, e2 b)
, cteTally(n) as (select top (isnull(datalength(#pString)/2,0))
row_number() over (order by (select null)) from e4)
, cteStart(n1) as (select 1 union all
select t.n+1 from cteTally t where substring(#pString,t.n,1) = #pDelimiter)
, ctelen(n1,l1) as(select s.n1
, isnull(nullif(charindex(#pDelimiter,#pString,s.n1),0)-s.n1,4000)
from cteStart s
)
select Itemnumber = row_number() over(order by l.n1)
, Item = substring(#pString, l.n1, l.l1)
from ctelen l;
go
the query
declare #str nvarchar(4000)= '01;05;06;03;02;10;11;';
with cte as (
select ItemNumber, Item
from dbo.DelimitedSplitN4K(#str,';')
where Item != ''
)
select combo=a.Item+';'+b.Item+';'+c.Item
from cte as a
inner join cte as b on a.ItemNumber<b.ItemNumber
inner join cte as c on b.ItemNumber<c.ItemNumber;
order by a.ItemNumber, b.ItemNumber, c.ItemNumber
ordered by ItemNumber results:
01;05;06
01;05;03
01;05;02
01;05;10
01;05;11
01;06;03
01;06;02
01;06;10
01;06;11
01;03;02
01;03;10
01;03;11
01;02;10
01;02;11
01;10;11
05;06;03
05;06;02
05;06;10
05;06;11
05;03;02
05;03;10
05;03;11
05;02;10
05;02;11
05;10;11
06;03;02
06;03;10
06;03;11
06;02;10
06;02;11
06;10;11
03;02;10
03;02;11
03;10;11
02;10;11
If you want to return a single string, pipe delimited then:
with cte as (
select ItemNumber, Item
from dbo.DelimitedSplitN4K(#str,';')
where Item != ''
)
select combo=stuff(
(select '|'+a.Item+';'+b.Item+';'+c.Item
from cte as a
inner join cte as b on a.ItemNumber<b.ItemNumber
inner join cte as c on b.ItemNumber<c.ItemNumber
order by a.ItemNumber, b.ItemNumber, c.ItemNumber
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
results:
01;05;06|01;05;03|01;05;02|01;05;10|01;05;11|01;06;03|01;06;02|01;06;10|01;06;11|01;03;02|01;03;10|01;03;11|01;02;10|01;02;11|01;10;11|05;06;03|05;06;02|05;06;10|05;06;11|05;03;02|05;03;10|05;03;11|05;02;10|05;02;11|05;10;11|06;03;02|06;03;10|06;03;11|06;02;10|06;02;11|06;10;11|03;02;10|03;02;11|03;10;11|02;10;11
splitting strings reference:
Tally OH! An Improved SQL 8K “CSV Splitter” Function
Splitting Strings : A Follow-Up - Aaron Bertrand
Split strings the right way – or the next best way
I had nearly the same query but resulted somehow different
Please check
/*
create table Combination (id char(2))
insert into Combination values ('01'),('05'),('06'),('03'),('02'),('10'),('11')
*/
select c1.id, c2.id, c3.id, c1.id + ';' + c2.id + ';' + c3.id Combination
from Combination c1, Combination c2, Combination c3
where
c2.id between c1.id and c3.id
and c1.id <> c2.id
and c2.id <> c3.id
order by c1.id, c2.id, c3.id
The output is

Match any substring of 4 consecutive characters

I'm looking for a join expression for matching strings from two different tables which both contain the same sub-string of 4 consective characters.
For example, the following should match:
String1 String2
-------- -----------
xxjohnyy abcjohnabc [common substring: "john"]
xxjohnyy johnny [common substring: "john"]
birdsings ravenbird [common substring: "bird"]
singbird a singer [common substring: "sing"]
This problem is very similar to finding the Longest Common Substring problem. You find the Longest Common Substring and then you pick those with common strings of 4. You will definitely find this link and this link helpful for you.
This is a very good exercise. Here is my attempt using Tally Table.
SQL Fiddle
;WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
SELECT TOP (
SELECT
CASE
WHEN MAX(LEN(String1)) > MAX(LEN(String2)) THEN MAX(LEN(String1))
ELSE MAX(LEN(String2))
END
FROM TestTable
)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E8
),
CteTable AS( -- Added an ID to uniquely identify each row
SELECT *, Id = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM TestTable
),
CteSubStr1 AS(
SELECT
ct.*,
substr = SUBSTRING(ct.String1, t.N, 4)
FROM CteTable ct
CROSS APPLY(
SELECT N FROM Tally
WHERE N <= LEN(ct.String1) - 3
)t
),
CteSubStr2 AS(
SELECT
ct.*,
substr = SUBSTRING(ct.String2, t.N, 4)
FROM CteTable ct
CROSS APPLY(
SELECT N FROM Tally
WHERE N <= LEN(ct.String2) - 3
)t
),
CteCommon AS(
SELECT * FROM CteSubStr1 c1
WHERE EXISTS(
SELECT 1 FROM CteSubStr2
WHERE
Id = c1.Id
AND substr = c1.substr
)
)
SELECT
String1, String2, substr
FROM (
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY LEN(substr) DESC)
FROM CteCommon
)t
WHERE RN = 1
Result
| String1 | String2 | substr |
|-----------|------------|--------|
| xxjohnyy | abcjohnabc | john |
| xxjohnyy | johnny | john |
| birdsings | ravenbird | bird |
| singbird | a singer | sing |
This part looks for the longest common substring.
SELECT
String1, String2, substr
FROM (
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY LEN(substr) DESC)
FROM CteCommon
)t
WHERE RN = 1
To get all the common substrings, use this instead:
SELECT * FROM CteCommon
;with pos as(select 1 as p
union all
select p + 1 from pos where p < 100),
uni as(select *, row_number() over(order by (select null)) id from t)
select t1.s1, t1.s2, ca.s
from uni t1
cross apply(select substring(t2.s2, p, 4) s
from uni t2
cross join pos
where t1.id = t2.id and
len(substring(t2.s2, p, 4)) = 4 and
t1.s1 like '%' + substring(t2.s2, p, 4) + '%')ca
Fiddlee http://sqlfiddle.com/#!3/bd4dd/16
Just change 100 to actual length of your columns...

How to select entries that are back to back?

How would I select c_user_id who have made back to back entries in a SQL Server 2008 database ?
Preferably people who have made more than 3 back to back entries like pras.chla#gmail.com below (sorting by c_id desc and c_id is an identity column)
c_id c_user_id c_entry
1427 xermadr.asdf#me.com 155575
1426 pras.chla#gmail.com 155829
1425 pras.chla#gmail.com 155826
1424 pras.chla#gmail.com 155828
1423 pras.chla#gmail.com 155830
1422 sdfe.qqol#gmail.com 155559
thanks again ?
One way
SELECT DISTINCT c_user_id
FROM tab t1
CROSS APPLY (SELECT 1 AS C
FROM (SELECT TOP 2 *
FROM tab t2
WHERE t2.c_id < t1.c_id
ORDER BY t2.c_id DESC) T
HAVING COUNT(c_user_id) = 2 AND COUNT(DISTINCT c_user_id) = 1 AND MIN(c_user_id) = t1.c_user_id) CA
Or another
WITH T AS
(
SELECT *,
ROW_NUMBER() OVER (order by c_id) -
ROW_NUMBER() OVER (PARTITION BY c_user_id order by c_id) AS Grp
FROM tab t1
)
SELECT DISTINCT c_user_id
FROM T
GROUP BY c_user_id, Grp
HAVING COUNT(*) >=3
;WITH someUserTableWithOrderNumber as
(
SELECT ROW_NUMBER ( ) OVER (order by c_id) OrderNumber,
c_id,
c_user_id,
c_entry
FROM someUserTable
)
SELECT DISTINCT a.c_user_id
FROM someUserTableWithOrderNumber a
JOIN someUserTableWithOrderNumber b on a.OrderNumber = b.OrderNumber + 1 AND a.c_user_id = b.c_user_id
JOIN someUserTableWithOrderNumber c on b.OrderNumber = c.OrderNumber + 1 AND b.c_user_id = c.c_user_id
JOIN someUserTableWithOrderNumber d on c.OrderNumber = d.OrderNumber + 1 AND c.c_user_id = d.c_user_id

Calculating value using previous value of a row in T-SQL

I got following table and want to calculate value of Column2 on each row using the value of the same column (Column2) from the previous row in a sql without using cursor or while loop.
Id Date Column1 Column2
1 01/01/2011 5 5 => Same as Column1
2 02/01/2011 2 18 => (1 + (value of Column2 from the previous row)) * (1 + (Value of Column1 from the current row)) i.e. (1+5)*(1+2)
3 03/01/2011 3 76 => (1+18)*(1+3) = 19*4
and so on
Any thoughts?
Assuming at least SQL Server 2005 for the recursive CTE:
;with cteCalculation as (
select t.Id, t.Date, t.Column1, t.Column1 as Column2
from YourTable t
where t.Id = 1
union all
select t.Id, t.Date, t.Column1, (1+t.Column1)*(1+c.Column2) as Column2
from YourTable t
inner join cteCalculation c
on t.Id-1 = c.id
)
select c.Id, c.Date, c.Column1, c.Column2
from cteCalculation c
I solved the problem, just mentioned.
This is my code:
;with cteCalculation as (
select t.Id, t.Column1, t.Column1 as Column2
from table_1 t
where t.Id = 1
union all
select t.Id, t.Column1, (1+t.Column1)*(1+c.Column2) as Column2
from table_1 t
inner join cteCalculation c
on t.Id-1 = c.id
),
cte2 as(
select t.Id, t.Column1 as Column3
from table_1 t
where t.Id = 1
union all
select t.Id, (select column2+1 from cteCalculation c where c.id = t.id) as Column3
from table_1 t
inner join cte2 c2
on t.Id-1 = c2.id
)
select c.Id, c.Column1, c.Column2, c2.column3
from cteCalculation c
inner join cte2 c2 on c.id = c2.id
The result is as I was expected:
1 5 5 5
2 2 18 19
3 3 76 77
Here is an example using ROW_NUMBER() if the Id's aren't necessarily in order:
;with DataRaw as (
select 1 as Id, '01/01/11' as Date, 5 as Column1 union
select 2 as Id, '02/01/11' as Date, 2 as Column1 union
select 4 as Id, '03/01/11' as Date, 3 as Column1
),
Data as (
select RowId = ROW_NUMBER() over (order by Id), Id, Date, Column1 from DataRaw
),
Data2 as (
select
RowId, id, Date, Column1, Column1 as Column2
from
Data d
where
RowId = 1
union all
select
d1.RowId, d1.id, d1.Date, d1.Column1, (1+d1.column1)*(1+d2.column2) as column2
from
Data d1
cross join
Data2 d2
where
d2.RowId + 1 = d1.RowId
)
select
Id, Date, Column1, Column2
from
Data2
edit: shoudld have read the question better...
Another go woudl be this:
;with DataRaw as (
select 1 as Id, '01/01/11' as Date, 5 as Column1 union
select 2 as Id, '02/01/11' as Date, 2 as Column1 union
select 4 as Id, '03/01/11' as Date, 3 as Column1
),
Data as (
select Ord = ROW_NUMBER() over (order by Id), Id, Date, Column1 from DataRaw
),
select -- formula goes here, using current and prev as datasources.
from data current
left join data prev on current.Ord = prev.Ord + 1 -- pick the previous row by adding 1 to the ordinal
I think a normal join to get to the previous row would be faster than a CTE. You;d have to check for yourself though.
Looks easier to me.
Good luck, GJ

Resources