How do you expand delimited rows in T-SQL - sql

I have a table that looks like this:
Emails | Data
---------------------------------------------------------------------------------
userA#email.com;userB#email.com;userC#email.com | Foo
userB#email.com | Bar
I want to parse out the delimited emails into their own rows such that it looks something like this:
Emails | Data
---------------------------------------------------------------------------------
userA#email.com | Foo
userB#email.com | Foo
userC#email.com | Foo
userB#email.com | Bar
I know there is a string_split function, but it would only work on the first column. I need some kind of join for this.
EDIT: Yes I know it breaks normal form, but bigquery for instance has an "unnest" function and has arrays as a datatype.

In the more recent versions of SQL Server, you can use string_split():
select s.value as email, t.data
from t cross apply
string_split(t.emails, ';') s;

Related

How to write SQL queries with respect to the following conditions?

I have a database table in which a column tags contain values such as:
"AutoMNRP, MNRP"
"Macro, MNRP"
"AutoMNRP, Micro"
"Macro, Micro"
where "...." represents a string.
I want to write a SQL query such that it filters out all results having MNRP tag in it. How can I do this?
I tried a not like operator of SQL on it, but if I want to remove MNRP tag, it also filters out AutoMNRP tag.
At the last of query I need results featuring -
"AutoMNRP, Micro"
"Macro, Micro".
(Results when MNRP is filtered out.)
The right answer to this is to fix your design, you shouldn't store the data like this (comma separated), because your table should be like (and the duplicates should be removed and handled too)
+----------+
| Data |
+----------+
| AutoMNRP |
| MNRP |
| Macro |
| MNRP |
| AutoMNRP |
| Micro |
| Macro |
| Micro |
+----------+
But... here is a way it may fit you requirements
;WITH T(Str) AS
(
SELECT 'AutoMNRP, MNRP' UNION ALL
SELECT 'Macro, MNRP' UNION ALL
SELECT 'AutoMNRP, Micro' UNION ALL
SELECT 'Macro, Micro'
)
SELECT Str
FROM T
WHERE Str NOT LIKE '% MNRP,%'
AND
Str NOT LIKE '%, MNRP';
Returns:
+-----------------+
| Str |
+-----------------+
| AutoMNRP, Micro |
| Macro, Micro |
+-----------------+
Live Demo
You also (as Larnu point to) do as
;WITH T(Str) AS
(
SELECT 'AutoMNRP, MNRP' UNION ALL
SELECT 'Macro, MNRP' UNION ALL
SELECT 'AutoMNRP, Micro' UNION ALL
SELECT 'Macro, Micro'
)
SELECT Str
FROM T
WHERE CONCAT(', ', Str, ',') NOT LIKE '%, MNRP,';
In SQL Server 2016+ you can use the STRING_SPLIT function. So you can multiply a record by the number of separated values in the tags column so you can then apply a simple WHERE clause. Something like this:
WITH cte AS
(
SELECT Id, SingleTag
FROM table_name CROSS APPLY STRING_SPLIT(tags, ',')
)
SELECT * FROM cte WHERE SingleTag = 'MNRP'

Use of LIKE in PostgreSQL with brackets

I try to be so specific as possible.
Currently I use MS SQL Server 2012.
A simplified table PlanMission contain these rows
|---------------------|---------------------|
| Bold_Id | MCurrentStates |
|---------------------|---------------------|
| 10776 |[original[scheme] |
|---------------------|---------------------|
| 10777 |[operative][inproc] |
|---------------------|---------------------|
| 10778 |[operative][closed] |
|---------------------|---------------------|
| 10779 |[operative][planopen]|
|---------------------|---------------------|
The Bold_id column is just an ID that is unique.
The column MCurrentStates is a VARCHAR column containing states as substrings.
A state is simply a string surrounded by brackets like [planopen]
So the column may be empty or have many states like example above.
IN MS SQL if I do like this
SELECT Bold_Id, MCurrentStates
FROM PlanMission
WHERE MCurrentStates LIKE '%[planopen]%'
it don't work. It just list all rows that are not empty in MCurrentStates.
It is solved by insert []
SELECT Bold_Id, MCurrentStates
FROM PlanMission
WHERE MCurrentStates LIKE '%[[]planopen]%'
That works fine.
Now I want to do this also for PostgreSQL.
Simple solution is to remove the brackets.
But my question is how can I do this with a query that is the same for both MS SQL and PostgreSQL?
Try:
SELECT Bold_Id, MCurrentStates
FROM PlanMission
WHERE MCurrentStates LIKE '%/[planopen/]%' ESCAPE '/';

Split string and Pivot Result - SQL Server 2012

I am using SQL Server 2012 and I have a table called XMLData that looks like this:
| Tag | Attribute |
|--------------|-----------------------------|
| tag1 | Cantidad=222¬ClaveProdServ=1|
| tag1 | Cantidad=333¬ClaveProdServ=2|
The column Tag has many repeated values, what is different is the column Attribute that has a string of attributes separated by "¬". I want to separate the list of attributes and then pivot the table so the tags are the column names.
The result I want is like this:
| tag1 | tag1 |
|-----------------|----------------|
| Cantidad=222 | Cantidad=333 |
| ClaveProdServ=1 | ClaveProdServ=2|
I have a custom made function that splits the string since SQL server 2012 doesn't have a premade function that does this. The function I have receives a
string as a parameter and the delimiter like so:
select *
from [dbo].[Split]('lol1,lol2,lol3,lol4',',')
this function will return this:
| item |
|--------|
| lol1 |
| lol2 |
| lol3 |
I can't find a way to pass the values of the column Attribute as parameter of this function, something like this:
SELECT *
FROM Split(A.Attribute,'¬'),XMLData A
And then put the values of the column Tag as the the column names for each set of Attributes
My magic crystal ball tells me, that you have - why ever - decided to do it this way and any comments about don't store CSV data are just annoying to you.
How ever...
If this is just a syntax issue, try it like this:
SELECT t.Tag
,t.Attribute
,splitted.item
FROM YourTable AS t
CROSS APPLY dbo.Split(t.Attribute,'¬') AS splitted
Otherwise show some more relevant details. Please read How to ask a good SQL question and How to create a MCVE

PostgreSQL Reverse LIKE

I need to test if any part of a column value is in a given string, instead of whether the string is part of a column value.
For instance:
This way, I can find if any of the rows in my table contains the string 'bricks' in column:
SELECT column FROM table
WHERE column ILIKE '%bricks%';
But what I'm looking for, is to find out if any part of the sentence "The ships hung in the sky in much the same way that bricks don’t" is in any of the rows.
Something like:
SELECT column FROM table
WHERE 'The ships hung in the sky in much the same way that bricks don’t' ILIKE '%' || column || '%';
So the row from the first example, where the column contains 'bricks', will show up as result.
I've looked through some suggestions here and some other forums but none of them worked.
Your simple case can be solved with a simple query using the ANY construct and ~*:
SELECT *
FROM tbl
WHERE col ~* ANY (string_to_array('The ships hung in the sky ... bricks don’t', ' '));
~* is the case insensitive regular expression match operator. I use that instead of ILIKE so we can use original words in your string without the need to pad % for ILIKE. The result is the same - except for words containing special characters: %_\ for ILIKE and !$()*+.:<=>?[\]^{|}- for regular expression patterns. You may need to escape special characters either way to avoid surprises. Here is a function for regular expressions:
Escape function for regular expression or LIKE patterns
But I have nagging doubts that will be all you need. See my comment. I suspect you need Full Text Search with a matching dictionary for your natural language to provide useful word stemming ...
Related:
IN vs ANY operator in PostgreSQL
PostgreSQL LIKE query performance variations
Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL
This query:
SELECT
regexp_split_to_table(
'The ships hung in the sky in much the same way that bricks don’t',
'\s' );
gives a following result:
| regexp_split_to_table |
|-----------------------|
| The |
| ships |
| hung |
| in |
| the |
| sky |
| in |
| much |
| the |
| same |
| way |
| that |
| bricks |
| don’t |
Now just do a semijoin against a result of this query to get desired results
SELECT * FROM table t
WHERE EXISTS (
SELECT * FROM (
SELECT
regexp_split_to_table(
'The ships hung in the sky in much the same way that bricks don’t',
'\s' ) x
) x
WHERE t.column LIKE '%'|| x.x || '%'
)

How to select everything that is NOT part of this string in database field?

First: I'm using Access 2010.
What I need to do is pull everything in a field out that is NOT a certain string. Say for example you have this:
00123457*A8V*
Those last 3 characters that are bolded are just an example; that portion can be any combination of numbers/letters and from 2-4 characters long. The 00123457 portion will always be the same. So what I would need to have returned by my query in the example above is the "A8V".
I have a vague idea of how to do this, which involved using the Right function, with (field length - the last position in that string). So what I had was
SELECT Right(Facility.ID, (Len([ID) - InstrRev([ID], "00123457")))
FROM Facility;
Logically in this mind it would work, however Access 2010 complains that I am using the Right function incorrectly. Can someone here help me figure this out?
Many thanks!
Why not use a replace function?
REPLACE(Facility.ID, "00123457", "")
You are missing a closing square bracket in here Len([ID)
You also need to reverse this "00123457" in InStrRev(), but you don't need InStrRev(), just InStr().
If I understand correctly, you want the last three characters of the string.
The simple syntax: Right([string],3) will yield the results you desire.
(http://msdn.microsoft.com/en-us/library/ms177532.aspx)
For example:
(TABLE1)
| ID | STRING |
------------------------
| 1 | 001234567A8V |
| 2 | 008765432A8V |
| 3 | 005671234A8V |
So then you'd run this query:
SELECT Right([Table1.STRING],3) AS Result from Table1;
And the Query returns:
(QUERY)
| RESULT |
---------------
| A8V |
| A8V |
| A8V |
EDIT:
After seeing the need for the end string to be 2-4 characters while the original, left portion of the string is 00123457 (8 characters), try this:
SELECT Right([Table1].[string],(Len([Table1].[string])-'8')) AS Result
FROM table1;