How to count a number of substring in a row via SQL? - sql

I am working on a database represeting a simple address book through MS Studio 2015 (C#) and MS SQL Server 2008. I successfully added 'insert row' and 'remove row' methods in my code. So I want to compose a query (a stored procedure) which counts a number of substring in every row.
For example, I have the database which includes a table called Contacts:
PersonID Name Surname City Phone
1 Alice Karlsson Gotheburg 69-58-12
2 Mark Morrow Stockholm 48-48-48
3 Katherine Karlsson Gotheburg 69-58-16
If I try to find and count 'th' in the table, I want to get the following the result:
PersonID Name Surname City Phone Count
3 Katherine Karlsson Gotheburg 69-58-16 2
1 Alice Karlsson Gotheburg 69-58-12 1
So I don't know how to do that. I've been googling for all the day but I didn't find the satisfying result. Here on the stackoverflow.com I find a solution returning the next result:
ColumnName ColumnValue
Contacts.City Gotheburg
Contacts.Name Katherine
Contacts.City Gotheburg
Please, give me any idea to compose a query returning the expected result.
Full-text search; is the expected result
UPD: 'th' is a substring I'm looking for in a row. So it should count "Agathe', 'th' and 'youth' the same way.

You should try following,
Select
PersonId,
Name,
Surname,
City,
Phone,
sum(count) as count
From
(
select
*,
(Len(name) - LEN(REPLACE(name, 'th', ' ')) +
Len(surname) - LEN(REPLACE(surname, 'th', ' ')) +
Len(city) - LEN(REPLACE(city, 'th', ' '))) as count
from Contacts
where name like '%th%' or surname like '%th%' or city like '%th%'
)T
Group by PersonId, Name, Surname, City, Phone
Order by 6 desc

Here what you are trying to achieve is fulltext search...
Please follow this link..
http://blog.sqlauthority.com/2008/09/05/sql-server-creating-full-text-catalog-and-index/
create a full text index
and use this script
select * from yourtable
where freetext (*,'your_search_item')

Try this way
select * from Contacts where Contacts.City like '%th%' or
Contacts.Name like '%th%'

You need to create a table valued function that loops on all rows column by column to seek the sub-string with a counter,
inside the loop you can use built in functions that help in seeking texts such as CHARINDEX('th',Name+Surname+City,0)
which gives the exact location of the sub-string inside the text ...

Related

Get all records where each words in string exists on any of the columns in a table

I am building a search functionality and need help with a postgres query. My use case is - When a string is an input, what is the best (optimized) way in postgres to get all records where each words in string exists on any of the columns in a table ?
Sample Table: (The table I am working with has 40 columns)
FName
Occupation
John
Engineer
Carlos
Doctor
Case 1: Given a string 'John Doctor', In this case it would return both the records.
Output:
FName
Occupation
John
Engineer
Carlos
Doctor
Case 2: Given a string 'John Engineer', it would only return 1 row
Output:
FName
Occupation
John
Engineer
Case 3: Given a string 'Carlos', it would return 1 row
Output:
FName
Occupation
Carlos
Doctor
Basically, you want to do following:
SELECT FName, Occupation
FROM yourtable
WHERE
'John' IN (FName, Occupation) OR
'Doctor' IN (FName, Occupation);
I don't know if this is already a sufficient answer for you because it's unclear if the logic to fetch the different names from your "search string" must be written as SQL query, too. I think that's a much better task for your application.
If this must also be done in pure SQL, you could use UNNEST to split your string.
Something like this:
WITH sub AS
(SELECT UNNEST(STRING_TO_ARRAY('John Doctor', ' ')) AS searchNames)
SELECT
DISTINCT y.FName, y.Occupation
FROM yourtable y, sub
WHERE
sub.searchNames IN (y.FName, y.Occupation);
This will split your string by spaces into the different names, i.e. you need to provide a search string in the form you have mentioned, with a space between the names.
This will produce the correct results according to your description.
We can verify this here: db<>fiddle1
This can be extended for as many column as needed. Let's for example add a further column col and search Test3 in this column, then the query will be like this:
SELECT FName, Occupation,col
FROM yourtable
WHERE 'John' IN (FName, Occupation, col)
OR 'Doctor' IN (FName, Occupation, col)
OR 'Test3' IN (FName, Occupation, col);
Or again with UNNEST like this:
WITH sub AS
(SELECT UNNEST(STRING_TO_ARRAY('John Doctor Test3', ' ')) AS searchNames)
SELECT
DISTINCT y.FName, y.Occupation, y.col
FROM yourtable y, sub
WHERE
sub.searchNames IN (y.FName, y.Occupation, y.col);
Try this here: db<>fiddle2
Use regexp match operator (case insensitive) and any to find the records that contain at least one of the words in the list.
select *
from the_table t
where t::text ~* any(string_to_array(the_words_list, ' '));
DB Fiddle demo

Query to get number of % sign = length of string in the next row in Oracle 10g

is there any SQL query in Oracle10G which can give the desired output as given required in below sample.
Query should print the name first and in the second row it should print the "%" equal in number with the length of the string.
Could you please help?
Below is the sample of table column
JIM
JOHN
MICHAEL
and the output should come like below :
JIM
%%%
JOHN
%%%%
MICHAEL
%%%%%%%
This would normally be considered an issue for presentation logic, not database logic. However, one option would be to use union all, and then length and rpad to get the correct number of % signs. You'd also need to establish a row number to keep the order together.
Here's one approach:
select name
from (
select name, rownum rn
from yourtable
union all
select rpad('%', length(name), '%') name, rownum + 1 rn
from yourtable ) t
order by rn, name
SQL Fiddle Demo
you can check this link
http://www.club-oracle.com/articles/oracle-pivoting-row-to-column-conversion-techniques-sql-166/
there are many options discussed, it will help

sql statement to select previous rows to a search param

Im after an sql statement (if it exists) or how to set up a method using several sql statements to achieve the following.
I have a listbox and a search text box.
in the search box, user would enter a surname e.g. smith.
i then want to query the database for the search with something like this :
select * FROM customer where surname LIKE searchparam
This would give me all the results for customers with surname containing : SMITH . Simple, right?
What i need to do is limit the results returned. This statement could give me 1000's of rows if the search param was just S.
What i want is the result, limited to the first 20 matches AND the 10 rows prior to the 1st match.
For example, SMI search:
Sives
Skimmings
Skinner
Skipper
Slater
Sloan
Slow
Small
Smallwood
Smetain
Smith ----------- This is the first match of my query. But i want the previous 10 and following 20.
Smith
Smith
Smith
Smith
Smoday
Smyth
Snedden
Snell
Snow
Sohn
Solis
Solomon
Solway
Sommer
Sommers
Soper
Sorace
Spears
Spedding
Is there anyway to do this?
As few sql statements as possible.
Reason? I am creating an app for users with slow internet connections.
I am using POSTGRESQL v9
Thanks
Andrew
WITH ranked AS (
SELECT *, ROW_NUMBER() over (ORDER BY surname) AS rowNumber FROM customer
)
SELECT ranked.*
FROM ranked, (SELECT MIN(rowNumber) target FROM ranked WHERE surname LIKE searchparam) found
WHERE ranked.rowNumber BETWEEN found.target - 10 AND found.target + 20
ORDER BY ranked.rowNumber
SQL Fiddle here. Note that the fiddle uses the example data, and I modified the range to 3 entries before and 6 entries past.
I'm assuming that you're looking for a general algorithm ...
It sounds like you're looking for a combination of finding the matches "greater than or equal to smith", and "less than smith".
For the former you'd order by surname and limit the result to 20, and for the latter you'd order by surname descending and limit to 10.
The two result sets can then be added together as arrays and reordered.
I think you need to use ROW_NUMBER() (see this link).
WITH cust1 AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY surname) as numRow FROM customer
)
SELECT c1.surname, c1.numRow, x.flag
FROM cust1 c1, (SELECT *,
case when numRow = (SELECT MIN(numRow) FROM cust1 WHERE surname='Smith') then 1 else 0 end as flag
FROM cust1) x
WHERE x.flag = 1 and c1.numRow BETWEEN x.numRow - 1 AND x.numRow + 1
ORDER BY c1.numRow
SQLFiddle here.
This works, but the flag finally isn't necessary and it would be a query like PinnyM posts.
A variation on #PinnyM's solution:
WITH ranked AS (
SELECT
*,
ROW_NUMBER() over (ORDER BY surname) AS rowNumber
FROM customer
),
minrank AS (
SELECT
*,
MIN(CASE WHEN surname LIKE searchparam THEN rowNumber END) OVER () AS target
FROM ranked
)
SELECT
surname
FROM minrank
WHERE rowNumber BETWEEN target - 10 AND target + 20
;
Instead of two separate calls to the ranked CTE, one to get the first match's row number and the other to read the results from, another CTE is introduced to serve both purposes. Can't speak for PostgreSQL but in SQL Server this might result in a better execution plan for the query, although in either case the real efficiency would still need to be verified by proper testing.

Any option except cursor in this kind of group by?

I have a sample data as:
Johnson; Michael, Surendir;Mishra, Mohan; Ram
Johnson; Michael R.
Mohan; Anaha
Jordan; Michael
Maru; Tushar
The output of the query should be:
Johnson; Michael 2
Mohan; Anaha 1
Michael; Jordon 1
Maru; Tushar 1
Surendir;Mishra 1
Mohan; Ram 1
As you can see it is print the count of each name separated by , but with a twist. We cannot simply do a groupby on full name because sometimes the name may contain middle name 1st initial and sometimes it may not. Eg. Johnson; Michael and Johnson; Michael R. are counted as single name and hence their count is 2. Further either Johnson; Michael should appear or Johnson; Michael R. should appear in resultset with count of 2 (not both because that would be repeated record)
The table contains names separated by , and it is not possible to denormalize it as it is LIVE and given to us by someone else.
Is there anyway to write a query for this without using cursor? I have around 3 million records in my DB and I have to support pagination etc also. What do you think would be the best way to achieve this?
This is why your data should be normalised.
;with cte as
(
select 1 as Item, 1 as Start, CHARINDEX(',',People+',' , 1) as Split,
People+',' as People
from YourHorribleTable
union all
select cte.Item+1, cte.Split+1, nullif(CHARINDEX(',',people, cte.Split+1),0), People as Split
from cte
where cte.Split<>0
)
select Person, COUNT(*)
from
(
select case when nullif(charindex (' ', person, 2+nullif(CHARINDEX(';', person),0)),0) is null then person
else substring(person,1,charindex (' ', person, 2+nullif(CHARINDEX(';', person),0)))
end as Person
from
(
select LTRIM(RTRIM( SUBSTRING(people, start,isnull(split,len(People)+1)-start))) as person
from cte
) v
where person<>''
) v
group by Person
order by COUNT(*) desc

How can an get count of the unique lengths of a string in database rows?

I am using Oracle and I have a table with 1000 rows. There is a last name field and
I want to know the lengths of the name field but I don't want it for every row. I want a count of the various lengths.
Example:
lastname:
smith
smith
Johnson
Johnson
Jackson
Baggins
There are two smiths length of five. Four others, length of seven. I want my query to return
7
5
If there were 1,000 names I expect to get all kinds of lengths.
I tried,
Select count(*) as total, lastname from myNames group by total
It didn't know what total was. Grouping by lastname just groups on each individual name unless it's a different last name, which is as expected but not what I need.
Can this be done in one SQL query?
SELECT Length(lastname)
FROM MyTable
GROUP BY Length(lastname)
select distinct(LENGTH(lastname)) from mynames;
Select count(*), Length(column_name) from table_name group by Length(column_name);
This will work for the different lengths in a single column.