need to bind values in place of ids(comma seperated) - sql

i want to replace ids with coresponding values
example:if column consist of ids 1,2
I need to replace 1 with "Product Videos",2 with "Installation" .
I can use case but not able to find a way to use case for comma seperated ids
SELECT BusinessFocus
,(
SELECT stuff((
SELECT ', ' + BusinessFocus
,CASE
WHEN BusinessFocus = 1
THEN 'Product Videos'
WHEN BusinessFocus = 2
THEN 'Installation Videos'
WHEN BusinessFocus = 3
THEN 'Other Videos'
END AS BFocusname
WHERE BusinessFocus IN (
SELECT val
FROM dbo.split(PartnerMaster.BusinessFocus, ',')
)
FOR XML PATH('')
), 1, 1, '')
) AS BusinessFocusNames
FROM PartnerMaster
Actual Result:
Expected Result:
1,3 | Product Videos,Installation Videos

If you just looking for convert '1,3' to 'Product Videos,Other Videos', you can use REPLACE as below-
DECLARE #BusinessFocus VARCHAR(200) = '1,3'
SELECT #BusinessFocus,
REPLACE(REPLACE(#BusinessFocus,'1','Product Videos'),'3','Other Videos')
Note: You can use one more REPLACE as shown.

If you are not storing these Business focus values somewhere else in table, make use of CTE.
Schema:
CREATE TABLE #PartnerMaster (BusinessFocus VARCHAR(100), BusinessFocusNames VARCHAR(MAX))
INSERT INTO #PartnerMaster (BusinessFocus)
SELECT '1,2' UNION ALL SELECT '3' UNION ALL SELECT '2,3'
Now do join with Charindex and make them comma separated list with For XML Path
;WITH CTE AS(
SELECT '1' BusinessFocus, 'Product Videos' BusinessFocusNames
UNION ALL
SELECT '2' BusinessFocus, 'Installation Videos' BusinessFocusNames
UNION ALL
SELECT '3' BusinessFocus, 'Other Videos' BusinessFocusNames
)
SELECT P.BusinessFocus
, STUFF((SELECT ','+C.BusinessFocusNames FROM CTE C
WHERE CHARINDEX(C.BusinessFocus,P.BusinessFocus)>0
FOR XML PATH('')),1,1,'') AS BusinessFocusNames
FROM #PartnerMaster P
Result:
+---------------+------------------------------------+
| BusinessFocus | BusinessFocusNames |
+---------------+------------------------------------+
| 1,2 | Product Videos,Installation Videos |
| 3 | Other Videos |
| 2,3 | Installation Videos,Other Videos |
+---------------+------------------------------------+

Related

How to match a string after a token (using regex)?

I'm trying to extract mail addresses after a token 'eaddr:'. So it would match the all occurrences in line entries, first consecutive string without spaces after that token: I tried:
SELECT regexp_substr(tab.entry, 'eaddr:\(.*?\)',1,1,'e',1)
from (
select 'String, email#domain.com' as entry
union
select 'eaddr:mail1#domain.com eaddr:mail2#domain.com sometext eaddr: mail3#domain.com some4354% text' as entry
union
select 'eaddr:mail5#domain.org' as entry
union
select 'Just a string' as entry
) tab
;
but it does not work. The correct result set is:
null
mail1#domain.com mail2#domain.com mail3#domain.com
mail5#domain.org
null
First of all, I suggest using a better regex to verify the email format. I am inspired by Gordon's SPLIT_TO_TABLE + LATERAL approach, and wrote some sample queries to fetch those emails from the entries.
If you want to get all the emails together, you can use this one:
with t as (
select 'String, email#domain.com' as entry
union
select 'eaddr:mail1#domain.com eaddr:mail2#domain.com sometext eaddr: mail3#domain.com some4354% text' as entry
union
select 'eaddr:mail5#domain.org' as entry
union
select 'Just a string' as entry
)
Select LISTAGG( regexp_substr( s.value, '[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}' ) ,' ' ) emails from t,
lateral SPLIT_TO_TABLE(t.entry, 'eaddr:') s
where s.seq > 1;
+---------------------------------------------------------------------+
| EMAILS |
+---------------------------------------------------------------------+
| mail1#domain.com mail2#domain.com mail3#domain.com mail5#domain.org |
+---------------------------------------------------------------------+
To get the exact result in your question, you can use the following query:
with t as (
select 'String, email#domain.com' as entry
union
select 'eaddr:mail1#domain.com eaddr:mail2#domain.com sometext eaddr: mail3#domain.com some4354% text' as entry
union
select 'eaddr:mail5#domain.org' as entry
union
select 'Just a string' as entry
)
select emails from
(
Select t.entry, s.*,
LISTAGG( regexp_substr( IFF(s.seq = 1, '', s.value ), '[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}' ) ,' ' )
OVER ( PARTITION BY s.seq ) emails
from t,
lateral SPLIT_TO_TABLE(t.entry, ' ') s )
where index = 1;
+----------------------------------------------------+
| EMAILS |
+----------------------------------------------------+
| NULL |
| mail1#domain.com mail2#domain.com mail3#domain.com |
| NULL |
| mail5#domain.org |
+----------------------------------------------------+
As far as I know, you can return only one match at a time from REGEXP_SUBSTR. The code below:
with tab(entry) as (
select 'String, email#domain.com' from dual
union
select 'eaddr:mail1#domain.com eaddr:mail2#domain.com sometext eaddr: mail3#domain.com some4354% text' from dual
union
select 'eaddr:mail5#domain.org' from dual
union
select 'Just a string' from dual
)
SELECT
regexp_substr(entry, 'eaddr:\s*(\S*)\s*',1,1,'i',1)
|| coalesce(' ' || regexp_substr(entry, 'eaddr:\s*(\S*)\s*',1,2,'i', 1), '')
|| coalesce(' ' || regexp_substr(entry, 'eaddr:\s*(\S*)\s*',1,3,'i', 1), '') as match,
regexp_count(entry, 'eaddr:\s*(\S*)\s*') as nmatches
from tab
gives the result below (using Oracle). You can use REGEXP_COUNT as shown to get the number of matches. If there are more than 3 email addresses, you can add more || coalesce( lines as needed.
P.S. I'm not sure what the 'e' flag does in your example. I'm guessing that is a Snowflake-specific value.
You need to split the strings, extract the emails, and then reaggregate. I don't have Snowflake on hand, but this or something similar should do:
select t.*, s.emails
from t left join lateral
(select list_agg(split(s.value, ' ')), ' ') as emails
from table(string_split_to_table(t.entry, 'eaddr:')) as s
) s;
I'm not 100% sure that Snowflake supports multiple-character delimiters, for instance. If that is the case, you can use:
select t.*, s.emails
from t left join lateral
(select list_agg(substr(s.value, 7), ' ') as emails
from table(string_split_to_table(t.entry, ' ')) as s
where value like 'eaddr:%'
) s;
Using Javascript UDF
create or replace function ext_mail(col VARCHAR)
returns varchar
language javascript
as
$$
var y = COL.match(/(?!eaddr):(\s+)?\w+#\w+/g);
if (y) {
ext_out = y.join(' ');
return ext_out.replace(/:|\s+/g,' ')
}
else return 'NULL'
$$
;
with t as (
select 'String, email#domain.com' as entry
union
select 'eaddr:mail1#domain.com eaddr:mail2#domain.com sometext eaddr: mail3#domain.com some4354% text' as entry
union
select 'eaddr:mail5#domain.org' as entry
union
select 'Just a string' as entry
) select ext_mail(ENTRY) from t;

distinct and sum if like

I have a table as the following
name
-----------
1#apple#1
2#apple#2
3#apple#4
4#box#4
5#box#5
and I want to get the result as:
name
--------------
apple 3
box 2
Thanks in advance for your help
This is what you need.
select
SUBSTRING(
name,
CHARINDEX('#', name) + 1,
LEN(name) - (
CHARINDEX('#', REVERSE(name)) + CHARINDEX('#', name)
)
),
count(1)
from
tbl
group by
SUBSTRING(
name,
CHARINDEX('#', name) + 1,
LEN(name) - (
CHARINDEX('#', REVERSE(name)) + CHARINDEX('#', name)
)
)
If your data does not contain any full stops (or periods depending on your vernacular), and the length of your string is less than 128 characters, then you can use PARSENAME to effectively split your string into parts, and extract the 2nd part:
DECLARE #T TABLE (Val VARCHAR(20));
INSERT #T (Val)
VALUES ('1#apple#1'), ('2#apple#2'), ('3#apple#4'),
('4#box#4'), ('5#box#5');
SELECT Val = PARSENAME(REPLACE(t.Val, '#', '.'), 2),
[Count] = COUNT(*)
FROM #T AS t
GROUP BY PARSENAME(REPLACE(t.Val, '#', '.'), 2);
Otherwise you will need to use CHARINDEX to find the first and last occurrence of # within your string (REVERSE is also needed to get the last position), then use SUBSTRING to extract the text between these positions:
DECLARE #T TABLE (Val VARCHAR(20));
INSERT #T (Val)
VALUES ('1#apple#1'), ('2#apple#2'), ('3#apple#4'),
('4#box#4'), ('5#box#5');
SELECT Val = SUBSTRING(t.Val, x.FirstPosition + 1, x.LastPosition - x.FirstPosition),
[Count] = COUNT(*)
FROM #T AS t
CROSS APPLY
( SELECT CHARINDEX('#', t.Val) ,
LEN(t.Val) - CHARINDEX('#', REVERSE(t.Val))
) AS x (FirstPosition, LastPosition)
GROUP BY SUBSTRING(t.Val, x.FirstPosition + 1, x.LastPosition - x.FirstPosition);
use case when
select case when name like '%apple%' then 'apple'
when name like '%box%' then 'box' end item_name,
count(*)
group by cas when name like '%apple%' then 'apple'
when name like '%box%' then 'box' end
No DBMS specified, so here is a postgres variant. The query does use regexps to simplify things a bit.
with t0 as (
select '1#apple#1' as value
union all select '2#apple#2'
union all select '3#apple#4'
union all select '4#box#4'
union all select '5#box#5'
),
trimmed as (
select regexp_replace(value,'[0-9]*#(.+?)#[0-9]*','\1') as name
from t0
)
select name, count(*)
from trimmed
group by name
order by name
DB Fiddle
Update
For Oracle DMBS, the query stays basically the same:
with t0 as (
select '1#apple#1' as value from dual
union all select '2#apple#2' from dual
union all select '3#apple#4' from dual
union all select '4#box#4' from dual
union all select '5#box#5' from dual
),
trimmed as (
select regexp_replace(value,'[0-9]*#(.+?)#[0-9]*','\1') as name
from t0
)
select name, count(*)
from trimmed
group by name
order by name
NAME | COUNT(*)
:---- | -------:
apple | 3
box | 2
db<>fiddle here
Update
MySQL 8.0
with t0 as (
select '1#apple#1' as value
union all select '2#apple#2'
union all select '3#apple#4'
union all select '4#box#4'
union all select '5#box#5'
),
trimmed as (
select regexp_replace(value,'[0-9]*#(.+?)#[0-9]*','$1') as name
from t0
)
select name, count(*)
from trimmed
group by name
order by name
name | count(*)
:---- | -------:
apple | 3
box | 2
db<>fiddle here
You can use case and group by to do the same.
select new_col , count(new_col)
from
(
select case when col_name like '%apple%' then 'apple'
when col_name like '%box%' then 'box'
else 'others' end new_col
from table_name
)
group by new_col
;

SQL Server: Merge two values with regexp

I am using an SQL Server database and have these following tables
Table "Data"
--------------------------------------------------------------------------------------------------|
| Id |col_1_type | col_1_name | col_2_type | col_2_name | col_3_type | col_3_name |
--------------------------------------------------------------------------------------------------|
| 1 |KI | Inflation Rate | KI | Currency Rate | MI | Government Spending |
--------------------------------------------------------------------------------------------------|
And i just want to make my result to be like this:
+----+------------------------+
| Id | results |
+----+------------------------+
| 1 | KI-Inflation Rate |
| 2 | KI-Currency Rate |
| 3 | MI-Government Spending |
+----+------------------------+
The column name is mandatory though, thats what made it complicated i guess?
i know you can merge 2 values or concatenate it, but i'm stuck on the column name such as col_1_name and col_2_type. Do i need to use regexp maybe?
Please try this-
select ROW_NUMBER() OVER (ORDER BY (select null)) id , results
from
(
SELECT CONCAT(col_1_type,'-',col_1_name) results
FROM [Data]
UNION ALL
SELECT CONCAT(col_2_type,'-',col_2_name)
FROM Data
UNION ALL
SELECT CONCAT(col_3_type,'-',col_3_name)
FROM Data
)o
Or this also
SELECT Id,results
FROM Data
CROSS apply
(VALUES (CONCAT(col_1_type,'-',col_1_name),1),(CONCAT(col_2_type,'-',col_2_name),2)
,(CONCAT(col_3_type,'-',col_3_name) ,3) ) cs (results,Id)
I would use of cross apply with the help of CTE and stuff()
;with cte as
(
select a.* from table t
cross apply (
values
(t.col_1_type, 'col_1'),
(t.col_1_name, 'col_1'),
(t.col_2_type, 'col_2'),
(t.col_2_name, 'col_2'),
(t.col_3_type, 'col_3'),
(t.col_3_name, 'col_3')
) a(name, id)
)
select distinct stuff(
(select '-'+name from cte where id= c.id for xml path('')),
1,1, ''
) [Results],
from cte c
EDIT :
Not sure about Id column but my guess that could be resolved by using ranking function
select row_number() over (order by (select 1)) Id,
cc.Results from
(
select distinct stuff(
(select '-'+id from cte where name = c.name for xml path('')),
1,1, ''
) [Results]
from cte c
) cc
Result :
Id Results
1 KI-Currency Rate
2 KI-Inflation Rate
3 MI-Government Spending
Sample data
IF OBJECT_ID('tempdb..#t') iS NOT NULL
DROP TABLE #t
IF OBJECT_ID('dbo.temp','U') iS NOT NULL
DROP TABLE temp
;With CTe(
Id ,col_1_type , col_1_name, col_2_type , col_2_name, col_3_type , col_3_name )
AS
(
SELECT 1,'KI','Inflation Rate','KI','Currency Rate','MI','Government Spending'
)
SELECT * INTO temp FROM CTe
Using Dynamic sql
DECLARE #Sqlstring nvarchar(max)
,#SQlQuery nvarchar(max)
;WITH cte
AS
(
SELECT COLUMN_NAME ,
((ROW_NUMBER()OVER(ORDER BY (SELECT NULL))-1)/2 )+1 AS BatchSeq
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='temp' AND COLUMN_NAME<>'Id'
)
SELECT #Sqlstring=STUFF((SELECT ', '+COLUMN_NAME FROM
(
SELECT DISTINCT '('+STUFF((SELECT ', '+COLUMN_NAME
FROM cte i
WHERE i.BatchSeq=o.BatchSeq FOR XML PATH ('')),1,1,'') +')' AS COLUMN_NAME
FROM cte o
)dt
FOR XML PATH ('')),1,1,'')
SET #SQlQuery='
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS ID,
CONCAT(col_type,''-'',Col_names) AS Result
FROM Temp
CROSS APPLY ( VALUES '+#Sqlstring+') dt(col_type,Col_names)'
PRINT #SQlQuery
EXEC(#SQlQuery)
Result
ID Result
-----------------------
1 KI-Inflation Rate
2 KI-Currency Rate
3 MI-Government Spending
Try like this
select (column1 || ' '|| column2) from table;
or
SELECT tablename.col_1_type + ' ' + tablename.col_1_name AS results;

Print result by merging records in a table

I have a table with name "PrintWord" and column name as col_letter and data in it is as follows:
"col_letter"
S
A
C
H
I
N
I would like to print the o/p from this table as:
SACHIN
Thanks!
DECLARE #t table
(
Name varchar(10)
)
INSERT INTO #t
SELECT 's' UNION ALL
SELECT 'a' UNION ALL
SELECT 'c' UNION ALL
SELECT 'h' UNION ALL
SELECT 'i' UNION ALL
SELECT 'n'
SELECT DISTINCT
stuff(
(
SELECT ' '+ [Name] FROM #t FOR XML PATH('')
),1,1,'')
FROM (SELECT DISTINCT Name FROM #t ) t
There is a hard-coded version :
SELECT col_letter
FROM PrintWord
ORDER BY
CASE col_letter
WHEN 'S' THEN 1
WHEN 'A' THEN 2
WHEN 'C' THEN 3
WHEN 'H' THEN 4
WHEN 'I' THEN 5
WHEN 'N' THEN 6
END
FOR XML PATH('')
You need an ORDER BY clause to guarantee the order of the letters.

JOIN three tables and aggregate data from multiple rows for every DISTINCT row in separate column

JOIN three tables and aggregate data from multiple rows for every DISTINCT row in separate column
i have a table where one item is mapped with multiple items.
Key 1 | Key 2
1 2
1 5
1 6
1 4
1 8
I have another table like this
Key 1 | ShortKey1Desc
1 'Desc short'
i have one more table where i have data like this
Key 1 | Description
1 'Desc a'
1 'Desc c'
1 'Desc aa'
1 'Desc tt'
i need to write a sql query for my view where table would be generated like this
Key 1 | AllKeys2ForKey1 | AllDescriptionsForKey1 | ShortKey1Desc
1 | 2;5;6;4;8 | Desc a; Desc c; Desc aa; Desc tt | Desc short
Key 1 is a string type field so i need to join them table using that string key
what i'm trying is to create a view for comfortable data access. need to create a query what will not take ages. i already tried to do it with Functions but it takes ages for load.
any help on this one would be highly appreciated. thanks a lot
Assuming that you are unable to change the data structures to make a more efficient query, this will work:
--Populate sample data
SELECT 1 as key1, 2 as key2 INTO #tbl1
UNION ALL SELECT 1, 5
UNION ALL SELECT 1, 6
UNION ALL SELECT 1, 4
UNION ALL SELECT 1, 8
SELECT 1 as key1, 'Desc short' as shortkeydesc INTO #tbl2
SELECT 1 as key1, 'Desc a' as [description] INTO #tbl3
UNION ALL SELECT 1, 'Desc c'
UNION ALL SELECT 1, 'Desc aa'
UNION ALL SELECT 1, 'Desc tt'
--Combine data into semi-colon separated lists
SELECT
key1
,STUFF(
(
SELECT
';' + CAST(t2.key2 AS VARCHAR(10))
FROM #tbl1 t2
WHERE t2.key1 = tbl1.key1
FOR XML PATH('')
), 1, 1, ''
)
,STUFF(
(
SELECT
';' + tbl2.shortkeydesc
FROM #tbl2 tbl2
WHERE tbl2.key1 = tbl1.key1
FOR XML PATH('')
), 1, 1, ''
)
,STUFF(
(
SELECT
';' + tbl3.[description]
FROM #tbl3 tbl3
WHERE tbl3.key1 = tbl1.key1
FOR XML PATH('')
), 1, 1, ''
)
FROM #tbl1 tbl1
GROUP BY tbl1.key1
to convert rows into one single result you will need to save values in a variable, below is sample code just to give you an idea
Declare #AllKeys2ForKey1 varchar(50)
set #AllKeys2ForKey1 = ''
SELECT #AllKeys2ForKey1 = #AllKeys2ForKey1 + cast([Key 2] as varchar(3)) + ','
FROM [AllKeys2ForKey1Table] where [KEY 1] = 1
Declare #AllDescriptionsForKey1 varchar(100)
set #AllDescriptionsForKey1 = ''
SELECT #AllKeys2ForKey1 = #AllKeys2ForKey1 + [Description] + ','
FROM [AllDescriptionsForKey1Table] where [KEY 1] = 1
Declare #ShortKey1Desc varchar(100)
set #ShortKey1Desc = ''
SELECT #ShortKey1Desc = #ShortKey1Desc + [ShortKey1Desc] + ','
FROM [ShortKey1DescTable] where [KEY 1] = 1
Select [KEY 1],
substring(#AllKeys2ForKey1,1,len(#AllKeys2ForKey1) - 1) as 'AllKeys2ForKey1 ',
substring(#AllDescriptionsForKey1,1,len(#AllDescriptionsForKey1) - 1) as 'AllDescriptionsForKey1',
substring(#ShortKey1Desc,1,len(#ShortKey1Desc) - 1) as 'ShortKey1Desc'
from Table where [KEY 1]= 1
You Must Write CLR Aggregate Function for Solving This Question.
for write CLR Aggregate Function :
1: Run Microsoft Visual Stadio
2: Create New Project
3: then Select Data Project
4: CLR Aggregate Function
After Create Your Aggregate Function Create Your Query Such as Below
Select A.Key1, OwnAggregateFn(B.Description), OwnAggregateFn(C.Key2), ...
From A
inner join B ON B.Key1 = A.Key1
inner join C ON C.Key1 = A.Key1
...
Group By A.Key1