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

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'

Related

Return only ALL CAPS strings in BigQuery

Pretty simple question, specific to BigQuery. I'm sure there's a command I'm missing. I'm used to using "collate" in another query which doesn't work here.
email
| -------- |
| eric#email.com |
| JOHN#EMAIL.COM |
| STACY#EMAIL.COM |
| tanya#email.com |
Desired return:
JOHN#EMAIL.COM,STACY#EMAIL.COM
Consider below
select *
from your_table
where upper(email) = email
If applied to sample data in your question - output is
In case you want the output as a comma separated list - use below
select string_agg(email) emails
from your_table
where upper(email) = email
with output
You can use below cte (which is exact data sample from your question) for testing purposes
with your_table as (
select 'eric#email.com' email union all
select 'JOHN#EMAIL.COM' union all
select 'STACY#EMAIL.COM' union all
select 'tanya#email.com'
)

How do you expand delimited rows in T-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;

Why can I cross join XMLTABLE? Why don't I have to cross apply?

I saw examples using XMLTABLE to sort comma-separated strings in Oracle. While we should never have to do this with a proper database design, it made me curious and there is one thing I don't understand at all:
Why am I allowed to cross join XMLTABLE referencing columns from the other table? I would expect to have to apply a lateral join (CROSS APPLY), but this doesn't seem needed. This query works:
select *
from
(
select 'b,a,d' as csv from dual
union all
select 'w,o,r,s,e' as csv from dual
) t
cross join xmltable
(
'if (contains($csv, ",")) then string-join(for $str in ora:tokenize($csv, ",") order by $str return $str, ",") else $csv'
passing t.csv as "csv"
columns sorted varchar2(4000) path '.'
) x
Result:
+-----------+-----------+
| CSV | SORTED |
+-----------+-----------+
| b,a,d | a,b,d |
| w,o,r,s,e | e,o,r,s,w |
+-----------+-----------+
I am passing t.csv which should not be accessible on the right side of the cross join in my opinion.
Can anybody explain what is happening here? I have come to think that Oracle muddles its way through here for some reason, thus violating the SQL standard. Am I right?
If you can explain what Oracle does here, this will certainly also explain why adding this
where sorted <> 'x'
leads to unexpected results. Unexpected to me that is :-)
Result with WHERE clause:
+-----------+--------+
| CSV | SORTED |
+-----------+--------+
| b,a,d | a,b,d |
| w,o,r,s,e | a,b,d |
+-----------+--------+
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=a9497bec423a3facbd29b49b3a40a350
Your "why" question might be difficult to answer; it is likely because that is how Oracle performed correlated queries using its legacy comma-join syntax and then adding ANSI syntax later and then in Oracle 12c added CROSS APPLY but finding documentation to back this up will be challenging.
However, if you can force the evaluation of the correlated CROSS JOIN (by performing a useless row specific operation, like generating the ROWNUM pseudo-column) before applying the WHERE filter clause then you can get the query to work:
WITH t ( csv ) AS (
select 'b,a,d' from dual union all
select 'w,o,r,s,e' from dual union all
select 'w,o,r,s,e,r' from dual
)
SELECT csv,
sorted
FROM (
select ROWNUM as id,
t.csv,
s.sorted
from t
CROSS JOIN xmltable (
'if (contains($csv, ",")) then string-join(for $str in ora:tokenize($csv, ",") order by $str return $str, ",") else $csv'
passing t.csv as "csv"
columns sorted varchar2(4000) path '.'
) s
)
WHERE sorted <> 'x';
Outputs:
CSV | SORTED
:---------- | :----------
b,a,d | a,b,d
w,o,r,s,e | e,o,r,s,w
w,o,r,s,e,r | e,o,r,r,s,w
db<>fiddle here

SQL LIKE using the same row value

I'm wondering how can I use a row value as a variable for my like statement? For example
ID | PID | DESCRIPTION
1 | 4124 | Hi4124
2 | 2451 | Test
3 | 1467 | Hello
4 | 9642 | Me9642
I have a table above, I want to return IDs 1 and 4 since DESCRIPTION contains PID.
I'm thinking it would be SELECT * from TABLE WHERE DESCRIPTION LIKE '%PID%' but I can't get it.
You can use CONCAT() to assemble the matching pattern, as in:
select *
from t
where description like concat('%', PID, '%')
We could also try using CHARINDEX here:
SELECT ID, PID, DESCRIPTION
FROM yourTable
WHERE CHARINDEX(PID, DESCRIPTION) > 0;
Demo
Note that I assume in the demo that the PID column is actually text, and not a numeric column. If PID be numeric, we might have to first use a cast in order to use CHARINDEX (or any of the methods given in the other answers).
Use the CONCAT SQL function
SELECT *
FROM TABLE
WHERE DESCRIPTION LIKE CONCAT('%', PID, '%')

How to a simple turn of the table (a pivot?) I can't seem to understand this simple concept

I have a query that returns a table of the form:
fastfill | slowfill
-------------------
x | y
query:
Select fastfill, slowfill
from cask_fills
where barcode=xyz and gaugetype=xyz;
It will only ever return one record.
I need the query to instead return it in the form
type | fills
----------------
fastfill | x
slowfill | y
But I can't seem to work out how to use the pivot command to do this (or indeed any command to do this).
All the tutorials and examples i have seen have involved aggregate functions to group data which i do not need in this example and I can't seem to work it out.
You are looking for UNPIVOT
I would use CROSS APPLY with VALUES to make it.
select v.*
from cask_fills
CROSS APPLY(VALUES ('fastfill',fastfill),('slowfill',slowfill)) v(type,fills)
where barcode=xyz and gaugetype=xyz;
Results:
| type | fills |
|----------|-------|
| fastfill | x |
| slowfill | y |
use union all
select 'fastfill' as type, fastfill as fill
from cask_fills
where barcode=xyz and gaugetype=xyz
union all
select 'slowfill ', slowfill
from cask_fills
where barcode=xyz and gaugetype=xyz
Everyone mentions UNPIVOT but noone posts the UNPIVOT query :
declare #cask_fills table (fastfill varchar(20),slowfill varchar(20))
insert into #cask_fills
values
('x','y')
SELECT type,fills
FROM
#cask_fills p
UNPIVOT (
fills FOR type IN (fastfill,slowfill)
) AS unpvt;
The result is :
type fills
fastfill x
slowfill y
The difference between UNPIVOT and the other answers is that UNPIVOT eliminates NULLs