How to look up numerical value of one column in a comma seperated values of another column in T-Sql? - sql-server-2012

I have a table with 3 fields as shown below and would like select only the rows in which [NewFacility] has the matching [Facility] value in it.
NewFacility field has the numerical values separated by comma.
Present table:
Item Facility NewFacility
Car 1 1
Van 1 2,4
Bus 3 2,4
Truck 4 2,3,4
Expected Result:
Item Facility NewFacility
Car 1 1
Truck 4 2,3,4
Table info

For your specific example:
SELECT Item
, Facility
, NewFacility
, ISNULL(i.value('n[1]','varchar(10)'), '')
, ISNULL(i.value('n[2]','varchar(10)'), '')
, ISNULL(i.value('n[3]','varchar(10)'), '')
FROM (SELECT Item
, Facility
, NewFacility
, CONVERT(XML,'<r><n>' + REPLACE(NewFacility, ',', '</n><n>') + '</n></r>') AS X
FROM [Table_Name]) Spt
CROSS APPLY Spt.X.nodes('/r') x(i)
WHERE Facility in (i.value('n[1]','varchar(10)')
, i.value('n[2]','varchar(10)')
, i.value('n[3]','varchar(10)'))
This works by converting the string to XML and accessing the comma-delimited string as a series of nodes. For each potential node you have to hard-code the position with another 'n[x]' value or come up with an iterative solution that pokes through all the positions if you have, say, an ever-expanding number of facilities.

Related

How to get data from db table, which has a column with mutiple values seperated by comma (,)

I have a table like below, called abc_table:
Id
Name
Tags
1
abc
1,4,5
2
aef
11,14,55
3
xyz
1,44,9
4
demo
1,98,4
Now, based on above data, I am looking for the name which has tag 1 and 4 / 1 or 4.
I tried using LIKE, in SQL operator, but it is not returning the expected output; I also tried with REGEX but that didn't work for me.
SELECT
ad.name, ad.tags
FROM
abc_table ad
AND CONCAT(',', ad.tags, ',') IN (',1,4,')
This will row1 data but not the row 4 data as 98 is in between the 1 and 4
One idea would be to use the LIKE operator and check all possible cases for each value, e.g.:
the tag is at the beginning and the tags column contains only one element (tags = '4')
the tag is at the beginning and the tags column contains further elements (tags LIKE '4,%')
the tag is in the middle and the tags column contains elements before and after (tags LIKE '%,4,%')
the tag is at the end and the tags column contains elements before it (tags LIKE '%,4')
Apply this for each tag value (1, 4) and combine the results correspondingly (if you want 1 and 4 => intersection and if 1 or 4 => union) and you should get the necessary result.
You can do like this to get tags that has (1 and 4) / (1 or 4)
SELECT name, tags
FROM abc_table
CROSS APPLY STRING_SPLIT(Tags, ',')
where value in (1,4)
group by name, tags;
Result :
name tags
abc 1,4,5
xyz 1,44,9
demo 1,98,4
STRING_SPLIT to Split comma-separated value string.
CROSS APPLY to transforms each list of tags and joins them with the original row
If you want to get tags that contains (1 and 4) OR (4 and 1) you can do it as follows :
SELECT name, tags
FROM abc_table
CROSS APPLY STRING_SPLIT(Tags, ',')
where value in (1,4)
group by name, tags
having count(*) = 2
result :
name tags
abc 1,4,5
demo 1,98,4
demo here

Advance Join Two Tables ON LIKE in SQL

So say I have two tables, each with one column (for simplicity):
Google Search Queries
Pop Culture Categories (Name, Event, etc)
Betty White Death
BTS
Grown Ups 2 ending
Miley Cyrus
My goal is to join both tables to match the Google Search Query, with the Pop Culture Category. For example:
Google Search Queries
Pop Culture Categories
Betty White Death
Betty White
Grown Ups 2 ending
Grown Ups 2
I attempted to do this by this Join Condition:
SELECT (columns)
FROM Google Search Queries
JOIN Pop Culture Categories ON Query LIKE CONCAT('%', Pop Culture, '%')
The problem is, it pulls in categories within the word of a query, such as BET (like the awards) or OWN (Oprah Winfrey Network). See below:
Google Search Queries
Pop Culture Categories
Betty White Death
Betty White
Betty White Death
BET
Grown Ups 2 ending
Grown Ups 2
Grown Ups 2 ending
OWN
Is there a way I could simply join the query to the full category, without having it pull out categories that are within a word of the search query?
You'll need (at a minimum) an ON condition that uses a regular expression. To do this right, you'd probably want to pre-process the search terms to extract relevant terms, correct common misspellings, etc. To simply match and avoid matching partial words, you can do this (the one at the bottom is what it needs - the others show why it's necessary to do it that way):
select REGEXP_INSTR( 'Betty White' , 'BET' , 1 , 1, 0 , 'im') > 0; -- Returns TRUE. Matches the substring - similar to ilike %BET%
select REGEXP_INSTR( 'Betty White' , '\\bBET\\b' , 1 , 1, 0 , 'im') > 0; -- Returns FALSE. Matches whole words only.
select REGEXP_INSTR( 'Grown Ups 2 ending' , '\\bGrown Ups 2\\b' , 1 , 1, 0 , 'im') > 0; -- Works okay if there are no extra spaces
select REGEXP_INSTR( 'Grown Ups 2 ending' , '\\bGrown\\s*Ups\\s*2\\b' , 1 , 1, 0 , 'im') > 0; -- Deals with extra spaces.
set col1 = 'Grown Ups 2 ending';
set col2 = 'Grown Ups 2';
select REGEXP_INSTR( $col1 , $col2 , 1 , 1, 0 , 'im') > 0; -- Won't work... Need to dynamically construct the pattern.
select REGEXP_INSTR( $col1 , '\\b' || replace($col2, ' ', '\\s*') || '\\b' , 1 , 1, 0 , 'im') > 0; -- This works as long as there's only one space on the Pop Culture side
If you are sure that the category name is always a subset of the Google Search Queries and the GSQ never starts with somehting different that is not the category, then you can just get rid of the initial % in the LIKE.

I need a query in postgres that returns the length of arrays but without taking empty values into consideration

so given the table:
id | names
===============
1 {John, , Wayne}
2 {Luke, Harold, }
3 {Bill}
4 {Will, , }
They don't have a standard and some values may come empty ( for example {Will, , }).
I tried:
SELECT array_length(names, 1)
FROM nameTable
But I get this:
names
======
3
3
1
3
and I want it to return:
names
======
2
2
1
1
So I need something which gives me the length only of the populated fields (empty spaces like ' ') shouldn't be counted.
You can remove the NULL values and then count:
array_length(array_remove(names, NULL), 1)
For one-dimensional arrays, I find that cardinality() is convenient:
cardinality(array_remove(names, NULL))

need to split a column value

I have a below table
id name total
1 a 2
2 b 3
3 c,d,e,f 15
Expected Output:-
id name total
1 a 2
2 b 3
3 c 15
4 d 15
5 e 15
5 f 15
I tried split function and also XML, but didn't work.
As you dont specify the DB name, Assuming SQL SERVER. You can try this one.
Working Example
SELECT A.[id],
Split.a.value('.', 'VARCHAR(100)') AS String,A.total
FROM (SELECT [id],
CAST ('<M>' + REPLACE([name], ',', '</M><M>') + '</M>' AS XML) AS String ,
[total]
FROM #t) AS A
CROSS APPLY String.nodes ('/M') AS Split(a);
Refer this article
Which version of SQL are you using?
The split function is for splitting a string of text, but what you are requesting is a change to the format of the table itself.
Your table has a tuple of id=3, name=c,d,e,f, total=15.
If you want id=3, name=c and so on, you have to change the data.
From the way your question is phrased, it implies that you want the data to be presented in a different way, but the id is the defining column which differentiates between rows in the database.
You could automatically generate a new table, in which case the split statement would be useful to get each element out of your comma separated record.
Once you have that list of items, assuming your id field is an identity field (auto incrementing), you could run an insert statement for each element.
You might be able to get the sort of output you're looking for using an inner select that splits the comma separated list of values, but you would need some procedural SQL (or T-SQL... you do not specify your SQL server) to iterate over the values and insert them into a new table.
If you do go down this route, the id values will have to be thrown away, and you would treat the list as just a raw data set.
EDIT: The example posted by Have No Display Name is about as close as you're going to get with the data in the form it is.
The IDs for the names 'c','d','e' and 'f' will all be 3, but your format will be very close.

List category/subcategory tree and display its sub-categories in the same row

I have a hierarchical table of Regions and sub-regions, and I need to list a tree of regions and sub-regions (which is easy), but also, I need a column that displays, for each region, all the ids of it's sub regions.
For example:
id name superiorId
-------------------------------
1 RJ NULL
2 Tijuca 1
3 Leblon 1
4 Gavea 2
5 Humaita 2
6 Barra 4
I need the result to be something like:
id name superiorId sub-regions
-----------------------------------------
1 RJ NULL 2,3,4,5,6
2 Tijuca 1 4,5,6
3 Leblon 1 null
4 Gavea 2 4
5 Humaita 2 null
6 Barra 4 null
I have done that by creating a function that retrieves a STUFF() of a region row,
but when I'm selecting all regions from a country, for example, the query becomes really, really slow, since I execute the function to get the region sons for each region.
Does anybody know how to get that in an optimized way?
The function that "retrieves all the ids as a row" is:
I meant that the function returns all the sub-region's ids as a string, separated by a comma.
The function is:
CREATE FUNCTION getSubRegions (#RegionId int)
RETURNS TABLE
AS
RETURN(
select stuff((SELECT CAST( wine_reg.wine_reg_id as varchar)+','
from (select wine_reg_id
, wine_reg_name
, wine_region_superior
from wine_region as t1
where wine_region_superior = #RegionId
or exists
( select *
from wine_region as t2
where wine_reg_id = t1.wine_region_superior
and (
wine_region_superior = #RegionId
)
) ) wine_reg
ORDER BY wine_reg.wine_reg_name ASC for XML path('')),1,0,'')as Sons)
GO
When we used to make these concatenated lists in the database we took a similar approach to what you are doing at first
then when we looked for speed
we made them into CLR functions
http://msdn.microsoft.com/en-US/library/a8s4s5dz(v=VS.90).aspx
and now our database is only responsible for storing and retrieving data
this sort of thing will be in our data layer in the application