Display a list of single values in SQL - sql

Let's say
SELECT 'Max' AS Foo
results into a result with one field. But there are also ways to give multiple values into your SQL application like
SELECT * FROM Customers WHERE Name IN ('Max','Tim')
question:
Is there also a way to display multiple rows like
SELECT ('Max','Tim') AS Foo

you can try like below
SELECT Stuff(
(SELECT N', ' + Name FROM table_name where Name IN ('Max','Tim')
FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'') as name

You can use union statement
select 'max' as foo
union
select 'tim' as foo

Related

How to extract the table name from a CREATE/UPDATE/INSERT statement in an SQL query?

I am trying to parse the table being created, inserted into or updated from the following sql queries stored in a table column.
Let's call the table column query. Following is some sample data to demonstrate variations in how the data could look like.
with sample_data as (
select 1 as id, 'CREATE TABLE tbl1 ...' as query union all
select 2 as id, 'CREATE OR REPLACE TABLE tbl1 ...' as query union all
select 3 as id, 'DROP TABLE IF EXISTS tbl1; CREATE TABLE tbl1 ...' as query union all
select 4 as id, 'INSERT /*some comment*/ INTO tbl2 ...' as query union all
select 5 as id, 'INSERT /*some comment*/ INTO tbl2 ...' as query union all
select 6 as id, 'UPDATE tbl3 SET col1 = ...' as query union all
select 7 as id, '/*some garbage comments*/ UPDATE tbl3 SET col1 = ...' as query union all
select 8 as id, 'DELETE tbl4 ...' as query
),
Following are the formats of the queries (we are trying to extract table_name ):
#1
some optional statements like drop table
CREATE some comments or optional statement like OR REPLACE TABLE table_name
everything else
#2
some optional statements like drop table
INSERT some comments INTO some comments table_name
#3
some optional statements like drop table
UPDATE some comments table_name
everything else
Regular Expression
To construct a suitable regex, let's start with the following relatively simple/readable version:
((CREATE( OR REPLACE)?|DROP) TABLE( IF EXISTS)?|UPDATE|DELETE|INSERT INTO) ([^\s\/*]+)
All the spaces above could be replaced with "at least one whitespace character", i.e. \s+. But we also need to allow comments. For a comment that looks like /*anything*/ the regex looks like \/\*.*\*\/ (where the comment characters are escaped with \ and "anything" is the .* in the middle). Given there could be multiple such comments, optionally separated by whitespace, we end up with (\s*\/\*.*\*\/\s*?)*\s+. Plugging this in everywhere there was a space gives:
((CREATE((\s*\/\*.*\*\/\s*?)*\s+OR(\s*\/\*.*\*\/\s*?)*\s+REPLACE)?|DROP)(\s*\/\*.*\*\/\s*?)*\s+TABLE((\s*\/\*.*\*\/\s*?)*\s+IF(\s*\/\*.*\*\/\s*?)*\s+EXISTS)?|UPDATE|DELETE|INSERT(\s*\/\*.*\*\/\s*?)*\s+INTO)(\s*\/\*.*\*\/\s*?)*\s+([^\s\/*]+)
One further refinement needs to be made: Bracketed expressions have been used for choices, e.g. (CHOICE1|CHOICE2). But this syntax includes them as capturing groups. Actually we only require one capturing group for the table name so we can exclude all the other capturing groups via ?:, e.g. (?:CHOICE1|CHOICE2). This gives:
(?:(?:CREATE(?:(?:\s*\/\*.*\*\/\s*?)*\s+OR(?:\s*\/\*.*\*\/\s*?)*\s+REPLACE)?|DROP)(?:\s*\/\*.*\*\/\s*?)*\s+TABLE(?:(?:\s*\/\*.*\*\/\s*?)*\s+IF(?:\s*\/\*.*\*\/\s*?)*\s+EXISTS)?|UPDATE|DELETE|INSERT(?:\s*\/\*.*\*\/\s*?)*\s+INTO)(?:\s*\/\*.*\*\/\s*?)*\s+([^\s\/*]+)
Online Regex Demo
Here's a demo of it working with your examples: Regex101 demo
SQL
The Google BigQuery documentation for REGEXP_EXTRACT says it will return the substring matched by the capturing group. So I'd expect something like this to work:
with sample_data as (
select 1 as id, 'CREATE TABLE tbl1 ...' as query union all
select 2 as id, 'CREATE OR REPLACE TABLE tbl1 ...' as query union all
select 3 as id, 'DROP TABLE IF EXISTS tbl1; CREATE TABLE tbl1 ...' as query union all
select 4 as id, 'INSERT /*some comment*/ INTO tbl2 ...' as query union all
select 5 as id, 'INSERT /*some comment*/ INTO tbl2 ...' as query union all
select 6 as id, 'UPDATE tbl3 SET col1 = ...' as query union all
select 7 as id, '/*some garbage comments*/ UPDATE tbl3 SET col1 = ...' as query union all
select 8 as id, 'DELETE tbl4 ...' as query
)
SELECT
*, REGEXP_EXTRACT(query, r"(?:(?:CREATE(?:(?:\s*\/\*.*\*\/\s*?)*\s+OR(?:\s*\/\*.*\*\/\s*?)*\s+REPLACE)?|DROP)(?:\s*\/\*.*\*\/\s*?)*\s+TABLE(?:(?:\s*\/\*.*\*\/\s*?)*\s+IF(?:\s*\/\*.*\*\/\s*?)*\s+EXISTS)?|UPDATE|DELETE|INSERT(?:\s*\/\*.*\*\/\s*?)*\s+INTO)(?:\s*\/\*.*\*\/\s*?)*\s+([^\s\/*]+)") AS table_name
FROM sample_data;
(The above is untested so please let me know in the comments if there are any issues.)
I think it really depends on your data, but you might find some success using an approach like this:
with data as (
select 1 as id, 'CREATE TABLE tbl1 ...' as query union all
select 2 as id, 'INSERT INTO tbl2 ...' as query union all
select 3 as id, 'UPDATE tbl3 ...' as query union all
select 4 as id, 'DELETE tbl4 ...' as query
),
splitted as (
select id, split(query, ' ') as query_parts from data
)
select
id,
case
when query_parts[safe_offset(0)] in('CREATE', 'INSERT') then query_parts[safe_offset(2)]
when query_parts[safe_offset(0)] in('UPDATE', 'DELETE') then query_parts[safe_offset(1)]
else 'Error'
end as table_name
from splitted
Of course this depends on the cleanliness and syntax in your query column. Also, if your table_name is qualified with project.table.dataset you would need to do further splitting.

ORACLE SQL CSV Column comparison

I have a table with CSV values as column. I want use that column in where clause to compare subset of CSV is present or not. For example Table has values like
1| 'A,B,C,D,E'
Query:
select id from tab where csv_column contains 'A,C';
This query should return 1.
How to achieve this in SQL?
You can handle this using LIKE, making sure to search for the three types of pattern for each letter/substring which you intend to match:
SELECT id
FROM yourTable
WHERE (csv_column LIKE 'A,%' OR csv_column LIKE '%,A,%' OR csv_column LIKE '%,A')
AND
(csv_column LIKE 'C,%' OR csv_column LIKE '%,C,%' OR csv_column LIKE '%,C')
Note that match for the substring A means that either A,, ,A, or ,A appears in the CSV column.
We could also write a structurally similar query using INSTR() in place of LIKE, which might even give a peformance boost over using wildcards.
there's probably something funky you can do with regular expressions but in simple terms... if A and C will always be in that order
csv_column LIKE '%A%C%'
otherwise
(csv_column LIKE '%A%' AND csv_column LIKE '%C%' )
If you don't want to edit your search string, this could be a way:
select *
from yourTable
where csv like '%' || replace('A,C', ',', '%') || '%'
For example:
with yourTable(id, csv) as (
select 1, 'A,B,C,D,E' from dual union all
select 2, 'A,C,D,E' from dual union all
select 3, 'B,C,D,E' from dual
)
select *
from yourTable
where csv like '%' || replace('A,C', ',', '%') || '%'
gives:
ID CSV
---------- ---------
1 A,B,C,D,E
2 A,C,D,E
Consider that this will only work if the characters in the search string have the same order of the CSV column; for example:
with yourTable(id, csv) as (
select 1, 'C,A,B' from dual
)
select *
from yourTable
here csv like '%' || replace('A,C', ',', '%') || '%'
will give no results
Why not store the values as separate columns, and then use simple predicate filtering?

Like Operator for checking multiple words

I'm struggling for a like operator which works for below example
Words could be
MS004 -- GTER
MS006 -- ATLT
MS009 -- STRR
MS014 -- GTEE
MS015 -- ATLT
What would be the like operator in Sql Server for pulling data which will contain words like ms004 and ATLT or any other combination like above.
I tried using multiple like for example
where column like '%ms004 | atl%'
but it didn't work.
EDIT
Result should be combination of both words only.
Seems you are looking for this.
`where column like '%ms004%' or column like '%atl%'`
or this
`where column like '%ms004%atl%'
;WITH LikeCond1 as (
SELECT 'MS004' as L1 UNION
SELECT 'MS006' UNION
SELECT 'MS009' UNION
SELECT 'MS014' UNION
SELECT 'MS015')
, LikeCond2 as (
SELECT 'GTER' as L2 UNION
SELECT 'ATLT' UNION
SELECT 'STRR' UNION
SELECT 'GTEE' UNION
SELECT 'ATLT'
)
SELECT TableName.*
FROM LikeCond1
CROSS JOIN LikeCond2
INNER JOIN TableName ON TableName.Column like '%' + LikeCond1.L1 + '%'
AND TableName.Column like '%' + LikeCond2.L2 + '%'
Try like this
select .....from table where columnname like '%ms004%' or columnname like '%atl%'

Is there a way to shorten this query?

I have a query like this:
SELECT Name,
REPLACE(RTRIM((
SELECT CAST(Score AS VARCHAR(MAX)) + ' '
FROM
(SELECT Name, Score
FROM table
WHERE
---CONDITIONS---
) AS InnerTable
WHERE (InnerTable.Name = OuterTable.Name) FOR XML PATH (''))),' ',', ') AS Scores
FROM table AS OuterTable
WHERE
---CONDITIONS---
GROUP BY Name;
As it can be seen, I am using the same set of conditions to derive the InnerTable and OuterTable. Is there a way to shorten this query? I am asking this because, sometime back, I saw a keyword USING in MySQL that simplified my life using which you can specify a query once and then use its alias for the rest of the query.
You could look at creating a Common Table Expression (CTE). That is your best bet for aliasing a select. Unfortunately I'm not sure how much shoerter it will make your query though it does prevent you from defining the where conditions twice. see below:
with temp as
(
SELECT Name, Score
FROM table
WHERE whatever = 'whatever'
)
SELECT Name,
REPLACE(RTRIM((
SELECT CAST(Score AS VARCHAR(MAX)) + ' '
FROM
(SELECT Name, Score
FROM temp ) AS InnerTable
WHERE (InnerTable.Name = OuterTable.Name) FOR XML PATH (''))),' ',', ') AS Scores
FROM temp AS OuterTable
GROUP BY Name;

SQL Server dynamic column list in contains function

I have a database table in SQL Server 2008 with 5 nvarchar(max) columns. We're using the CONTAINS function to look for text in these columns.
We can look in all five columns using this kind of query:
SELECT *
FROM SomeTable ST
WHERE CONTAINS( (ST.ColumnA, ST.ColumnB, ST.ColumnC, ST.ColumnD, ST.ColumnE) , '"Strawberry"')
Now we want to search for text for one or more of these columns dynamically. For example, only look in ColumnB and ColumnE. I tried using a CASE statement, but I couldn't.
The only solution I can think of is dynamic SQL, but I'd prefer to avoid this. (The full query is very complicated.) Is there a way to do this without using dynamic SQL?
The only way I can think of to avoid using dynamic SQL involves using a temp table and if statements and stored proc. Have a stored proc with parameters for each of the columns that include the text to search that columns for. Create a temp table or table variable to store interim results.
Have five different if statements one for each possible column. In each if insert to the temp table if the variable is not null. something like:
IF #ColA is not null
BEGIN
INSERT INTO #temp
SELECT * FROM SomeTable ST
WHERE CONTAINS( (ST.ColumnA) , #ColA)
END
At the end select from the temp table to show your result.
This only works well though if you don't have very many columns and it would be unlikely that more will be added. Frankly, the fact that you have multiple columns you need to do full text search for the same search string indicates to me that you may have a basic problem with the database design.
I just can think in a solution like this, but this doesn't use full text Search... let me know if it is useful for you.
SELECT *
FROM SomeTable ST
WHERE 1=1
AND ((#colA = '') OR ST.ColumnA LIKE #colA)
AND ((#colB = '') OR ST.ColumnB LIKE #colB)
AND ((#colC = '') OR ST.ColumnC LIKE #colC)
AND ((#colD = '') OR ST.ColumnD LIKE #colD)
AND ((#colE = '') OR ST.ColumnE LIKE #colE)
The search_on parameters would be bit (0, 1) so, when set on 1, the search on that column would be activated.
You could UNION the 5 individual cases, then PIVOT and concatenate. I'm not sure it's any better than dynamic SQL.
You would end up with something like:
SET #FindKey = '%B%E%' -- This is your search which field criteria
WITH RESULTS1 AS (
SELECT PK, 'A' AS Col
FROM SomeTable ST
WHERE #FindKey LIKE '%A%' AND CONTAINS(ST.ColumnA, '"Strawberry"')
UNION
SELECT PK, 'B' AS Col
FROM SomeTable ST
WHERE #FindKey LIKE '%B%' AND CONTAINS(ST.ColumnB, '"Strawberry"')
UNION
SELECT PK, 'C' AS Col
FROM SomeTable ST
WHERE #FindKey LIKE '%C%' AND CONTAINS(ST.ColumnC, '"Strawberry"')
UNION
SELECT PK, 'D' AS Col
FROM SomeTable ST
WHERE #FindKey LIKE '%D%' AND CONTAINS(ST.ColumnD, '"Strawberry"')
UNION
SELECT PK, 'E' AS Col
FROM SomeTable ST
WHERE #FindKey LIKE '%E%' AND CONTAINS(ST.ColumnE, '"Strawberry"')
)
,RESULTS2 AS (
SELECT PK, ISNULL([A], '') + ISNULL([B], '') + ISNULL([C], '') + ISNULL([D], '') + ISNULL([E], '') AS FoundKey
FROM RESULTS PIVOT ( MIN(Col) FOR Col IN ([A], [B], [C], [D], [E]) ) AS pvt
)
SELECT *
FROM SomeTable
INNER JOIN RESULTS2
ON RESULTS2.PK = SomeTable.PK
WHERE RESULTS2.FoundKey LIKE #FindKey