Sql - which letter of the alphabet is not in the names - sql

I am a beginner at SQL
I need to find out which letter of the alphabet is not in a list of names as first character.
How can I do that?
To look for a specific letter I can use the LIKE operator. However I don't know what to use to look for the letter that is not in the list....
The query I have to find the different first letter of the emailadresses is:
select left(EmailAddress,1), count(left(EmailAddress,1)
from Customers
order by left(EmailAdress,1)
There is no e-mail adress that start with a U.
But which query I can use to get that result?

You'd need a table of all letters. You can use an ad hoc derived table using UNION ALL and FROM-less SELECTs. One method then is to use a correlated subquery and NOT EXISTS to check for letters not in the first position of any e-mail address.
SELECT l.letter
FROM (SELECT 'a' letter
UNION ALL
SELECT 'b' letter
...
SELECT 'y' letter
UNION ALL
SELECT 'z' letter) l
WHERE NOT EXISTS (SELECT *
FROM customers c
WHERE left(lower(emailaddress), 1) = l.letter);

One could left join to letters and filter the unmatched.
The example below uses a recursive CTE to generate the letters.
WITH RCTE_LETTERS AS
(
SELECT CHAR(ASCII('a')) AS Letter, ASCII('a') AS Code
UNION ALL
SELECT CHAR(code+1), code+1
FROM RCTE_LETTERS
WHERE code < ASCII('z')
)
, CTE_FIRST_LETTERS AS
(
SELECT DISTINCT
LOWER(LEFT(EmailAddress,1)) AS FirstLetter
FROM Customers
)
SELECT l.Letter
FROM RCTE_LETTERS AS l
LEFT JOIN CTE_FIRST_LETTERS AS fl
ON fl.FirstLetter = l.Letter
WHERE fl.FirstLetter IS NULL
ORDER BY l.Letter;
Or use an EXCEPT
WITH RCTE_LETTERS AS
(
SELECT CHAR(ASCII('a')) AS Letter, ASCII('a') AS Code
UNION ALL
SELECT CHAR(code+1), code+1
FROM RCTE_LETTERS
WHERE code < ASCII('z')
)
, CTE_FIRST_LETTERS AS
(
SELECT DISTINCT
LOWER(LEFT(EmailAddress,1)) AS FirstLetter
FROM Customers
)
SELECT Letter FROM RCTE_LETTERS
EXCEPT
SELECT FirstLetter FROM CTE_FIRST_LETTERS
ORDER BY Letter

USE AdventureWorks2014
GO
SELECT left(e.EmailAddress,1) AS Letter, COUNT(left(e.EmailAddress,1)) AS CountEmailLetter
FROM Person.Person p INNER JOIN person.EmailAddress e ON p.BusinessEntityID = e.BusinessEntityID
WHERE left(e.EmailAddress,1) NOT IN
(
SELECT Char(number+65)
FROM master.dbo.spt_values
WHERE name IS NULL AND number < 26
)
GROUP BY left(e.EmailAddress,1)
ORDER BY left(e.EmailAddress,1)

Related

Query rows where first_name contains at least 2 vowels, and the number of occurences of each vowel is equal

I have the following problem: Show all rows in table where column first_name contains at least 2 vowels (a, e, i, o, u), and the number of occurences of each vowel is the same.
Valid example: Alexander, "e" appears 2 times, "a" appears 2 times. That is coreect.
Invalid example: Jonathan, it has 2 vowels (a, o), but "o" appears once, and "a" appears twice, the number of occurences is not equal.
I've solved this problem by calculating each vowel, and then verify every case (A E, A I, A O etc. Shortly, each combination of 2, 3, 4, 5). With that solution, I have a very long WHERE. Is there any shorter way and more elegant and simple?
This is how I solved it in TSQL in MS SQL Server 2019.
I know its not exactly what you wanted. Just an interesting thing to try. Thanks for that.
DROP TABLE IF EXISTS #Samples
SELECT n.Name
INTO #Samples
FROM
(
SELECT 'Ravi' AS Name
UNION
SELECT 'Tim'
UNION
SELECT 'Timothe'
UNION
SELECT 'Ian'
UNION
SELECT 'Lijoo'
UNION
SELECT 'John'
UNION
SELECT 'Jami'
) AS n
SELECT g.Name,
IIF(MAX (g.Repeat) = MIN (g.Repeat) AND SUM (g.Appearance) >= 2, 'Valid', 'Invalid') AS Validity
FROM
(
SELECT v.value,
s.Name,
SUM (LEN (s.Name) - LEN (REPLACE (s.Name, v.value, ''))) AS Repeat,
SUM (IIF(s.Name LIKE '%' + v.value + '%', 1, 0)) AS Appearance
FROM STRING_SPLIT('a,e,i,o,u', ',') AS v
CROSS APPLY #Samples AS s
GROUP BY v.value,
s.Name
) AS g
WHERE g.Repeat > 0
GROUP BY g.Name
Output
we can replace STRING_SPLIT with a temp table for supporting lower versions
DROP TABLE IF EXISTS #Vowels
SELECT C.Vowel
INTO #Vowels
FROM
(
SELECT 'a' AS Vowel
UNION
SELECT 'e'
UNION
SELECT 'i'
UNION
SELECT 'o'
UNION
SELECT 'u'
) AS C
SELECT g.Name,
IIF(MAX (g.Repeat) = MIN (g.Repeat) AND SUM (g.Appearance) >= 2, 'Valid', 'Invalid') AS Validity
FROM
(
SELECT v.Vowel,
s.Name,
SUM (LEN (s.Name) - LEN (REPLACE (s.Name, v.Vowel, ''))) AS Repeat,
SUM (IIF(s.Name LIKE '%' + v.Vowel + '%', 1, 0)) AS Appearance
FROM #Vowels AS v
CROSS APPLY #Samples AS s
GROUP BY v.Vowel,
s.Name
) AS g
WHERE g.Repeat > 0
GROUP BY g.Name
From Oracle 12, you can use:
SELECT name
FROM table_name
CROSS JOIN LATERAL(
SELECT 1
FROM (
-- Step 2: Count the frequency of each vowel
SELECT letter,
COUNT(*) As frequency
FROM (
-- Step 1: Find all the vowels
SELECT REGEXP_SUBSTR(LOWER(name), '[aeiou]', 1, LEVEL) AS letter
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT(LOWER(name), '[aeiou]')
)
GROUP BY letter
)
-- Step 3: Filter out names where the number of vowels are
-- not equal or the vowels do not occur at least twice
-- and there are not at least 2 different vowels.
HAVING MIN(frequency) >= 2
AND MIN(frequency) = MAX(frequency)
AND COUNT(*) >= 2
);
Which, for the sample data:
CREATE TABLE table_name (name) AS
SELECT 'Alexander' FROM DUAL UNION ALL
SELECT 'Johnaton' FROM DUAL UNION ALL
SELECT 'Anna' FROM DUAL;
Outputs:
NAME
Alexander
db<>fiddle here

BigQuery Union Distinct Where Value not in Preceding DataSet

I am trying to reconcile some student database with GSuite emails, where usernames have been created inconsistently for years.
The gist of the query I am trying to make on BigQuery is:
Match Emails to Students from email Pattern 1 and union with
Match Emails to Students from email Pattern 2 and union with
Emails not in 1 an 2.
Or in SQL:
with mymatches as (
with emaildataset as (
select 'testA' as col
union all
select 'testB'
union all
select 'testC'
union all
select 'testD'
)
select * from emaildataset where col like '%A'
union distinct
select * from emaildataset where col like '%B'
),
emaildataset2 as (
select 'testA' as col
union all
select 'testB'
union all
select 'testC'
union all
select 'testD'
)
select * from mymatches
union distinct
select * from emaildataset2 where emaildataset2.col not in (select col from mymatches)
This runs happily, but when I run the real code, then I'm getting duplicates.
The real code is now:
with matchedEmails as (
with g as (
select * from gsuite.StudentUsers
union all
select * from gsuite.AlumniUsers
)
select
std.STDCODE,
g.*
from g
inner join quick.all_students_alumni as std
on split(lower(g.Email), '#')[offset(0)] = split(quick.studentEmail(std.FNAME, std.MNAME, std.LNAME, std.STATUSTYPE), '#')[offset(0)]
where g.OU like '/Student%' or OU like '/Alumni%'
union distinct select
std.STDCODE,
g.*
from g
inner join quick.all_students_alumni as std
on split(lower(g.Email), '#')[offset(0)] = split(quick.studentEmail(std.FNAME, '', std.LNAME, std.STATUSTYPE), '#')[offset(0)]
where g.OU like '/Student%' or OU like '/Alumni%'
)
select * from matchedEmails
union distinct select
'NOT MATCHED' as STDCODE,
g.*
from (
select * from gsuite.StudentUsers
union all
select * from gsuite.AlumniUsers
) as g
where g.Email not in (select Email from matchedEmails)
and g.OU like '/Student%' or OU like '/Alumni%'
As a result though, I am getting duplicates in the Email column, which--based on my knowledge and test above--should not be, due to the where g.Email not in (select Email from matchedEmails) clause.
Am I doing something wrong?
I think, very last WHERE clause should be fixed to look like below
where g.Email not in (select Email from matchedEmails)
and (g.OU like '/Student%' or OU like '/Alumni%')
As you can see - the brackets around g.OU like '/Student%' or OU like '/Alumni%' were missing
it might be something else too that still need to be fixed - but this answers you below questions
As a result though, I am getting duplicates in the Email column, which--based on my knowledge and test above--should not be, due to the where g.Email not in (select Email from matchedEmails) clause.

SQL: Return a count of 0 with count(*)

I am using WinSQL to run a query on a table to count the number of occurrences of literal strings. When trying to do a count on a specific set of strings, I still want to see if some values return a count of 0. For example:
select letter, count(*)
from table
where letter in ('A', 'B', 'C')
group by letter
Let's say we know that 'A' occurs 3 times, 'B' occurs 0 times, and 'C' occurs 5 times. I expect to have a table returned as such:
letter count
A 3
B 0
C 5
However, the table never returns a row with a 0 count, which results like so:
letter count
A 3
C 5
I've looked around and saw some articles mentioning the use of joins, but I've had no luck in correctly returning a table that looks like the first example.
You can create an in-line table containing all letters that you look for, then LEFT JOIN your table to it:
select t1.col, count(t2.letter)
from (
select 'A' AS col union all select 'B' union all select 'C'
) as t1
left join table as t2 on t1.col = t2.letter
group by t1.col
on many platforms you can now use the values statement instead of union all to create your "in line" table - like this
select t.letter, count(mytable.letter)
from ( values ('A'),('B'),('C') ) as t(letter)
left join mytable on t.letter = mytable.letter
group by t.letter
I'm not that familiar with WinSQL, but it's not pretty if you don't have the values that you want in the left most column in a table somewhere. If you did, you could use a left join and a conditional. Without it, you can do something like this:
SELECT all_letters.letter, IFNULL(letter_count.letter_count, 0)
FROM
(
SELECT 'A' AS letter
UNION
SELECT 'B' AS letter
UNION
SELECT 'C' AS letter
) all_letters
LEFT JOIN
(SELECT letter, count(*) AS letter_count
FROM table
WHERE letter IN ('A', 'B', 'C')
GROUP BY letter) letter_count
ON all_letters.letter = letter_count.letter

How to convert only first letter uppercase without using Initcap in Oracle?

Is there a way to convert the first letter uppercase in Oracle SQl without using the Initcap Function?
I have the problem, that I must work with the DISTINCT keyword in SQL clause and the Initcap function doesn´t work.
Heres is my SQL example:
select distinct p.nr, initcap(p.firstname), initcap(p.lastname), ill.describtion
from patient p left join illness ill
on p.id = ill.id
where p.deleted = 0
order by p.lastname, p.firstname;
I get this error message: ORA-01791: not a SELECTed expression
When SELECT DISTINCT, you can't ORDER BY columns that aren't selected. Use column aliases instead, as:
select distinct p.nr, initcap(p.firstname) fname, initcap(p.lastname) lname, ill.describtion
from patient p left join illness ill
on p.id = ill.id
where p.deleted = 0
order by lname, fname
this would do it, but i think you need to post your query as there may be a better solution
select upper(substr(<column>,1,1)) || substr(<column>,2,9999) from dual
To change string to String, you can use this:
SELECT
regexp_replace ('string', '[a-z]', upper (substr ('string', 1, 1)), 1, 1, 'i')
FROM dual;
This assumes that the first letter is the one you want to convert. It your input text starts with a number, such as 2 strings then it won't change it to 2 Strings.
You can also use the column number instead of the name or alias:
select distinct p.nr, initcap(p.firstname), initcap(p.lastname), ill.describtion
from patient p left join illness ill
on p.id = ill.id
where p.deleted = 0
order by 3, 2;
WITH inData AS
(
SELECT 'word1, wORD2, word3, woRD4, worD5, word6' str FROM dual
),
inRows as
(
SELECT 1 as tId, LEVEL as rId, trim(regexp_substr(str, '([A-Za-z0-9])+', 1, LEVEL)) as str
FROM inData
CONNECT BY instr(str, ',', 1, LEVEL - 1) > 0
)
SELECT tId, LISTAGG( upper(substr(str, 1, 1)) || substr(str, 2) , '') WITHIN GROUP (ORDER BY rId) AS camelCase
FROM inRows
GROUP BY tId;

Check palindrome without using string functions with condition

I have a table EmployeeTable.
If I want only that records where employeename have character of 1 to 5
will be palindrome and there also condition like total character is more then 10 then 4 to 8 if character less then 7 then 2 to 5 and if character less then 5 then all char will be checked and there that are palindrome then only display.
Examples :- neen will be display
neetan not selected
kiratitamara will be selected
I try this something on string function like FOR first case like name less then 5 character long
SELECT SUBSTRING(EmployeeName,1,5),* from EmaployeeTable where
REVERSE (SUBSTRING(EmployeeName,1,5))=SUBSTRING(EmployeeName,1,5)
I want to do that without string functions,
Can anyone help me on this?
You need at least SUBSTRING(), I have a solution like this:
(In SQL Server)
DECLARE #txt varchar(max) = 'abcba'
;WITH CTE (cNo, cChar) AS (
SELECT 1, SUBSTRING(#txt, 1, 1)
UNION ALL
SELECT cNo + 1, SUBSTRING(#txt, cNo + 1, 1)
FROM CTE
WHERE SUBSTRING(#txt, cNo + 1, 1) <> ''
)
SELECT COUNT(*)
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY cNo DESC) as cRevNo
FROM CTE t1 CROSS JOIN
(SELECT Max(cNo) AS strLength FROM CTE) t2) dt
WHERE
dt.cNo <= dt.strLength / 2
AND
dt.cChar <> (SELECT dti.cChar FROM CTE dti WHERE dti.cNo = cRevNo)
The result will shows the count of differences and 0 means no differences.
Note :
Current solution is Non-Case-Sensitive for change it to a Case-Sensitive you need to check the strings in a case-sensitive collation like Latin1_General_BIN
You can use this solution as a SVF or something like that.
I dont realy understand why you dont want to use string functions in your query, but here is one solution. Compute everything beforehand:
Add Column:
ALTER TABLE EmployeeTable
ADD SubString AS
SUBSTRING(EmployeeName,
(
CASE WHEN LEN(EmployeeName)>10
THEN 4
WHEN LEN(EmployeeName)>7
THEN 2
ELSE 1 END
)
,
(
CASE WHEN LEN(EmployeeName)>10
THEN 8
WHEN LEN(EmployeeName)>7
THEN 5
ELSE 5 END
)
PERSISTED
GO
ALTER TABLE EmployeeTable
ADD Palindrome AS
REVERSE(SUBSTRING(EmployeeName,
(
CASE WHEN LEN(EmployeeName)>10
THEN 4
WHEN LEN(EmployeeName)>7
THEN 2
ELSE 1 END
)
,
(
CASE WHEN LEN(EmployeeName)>10
THEN 8
WHEN LEN(EmployeeName)>7
THEN 5
ELSE 5 END
)) PERSISTED
GO
Then your query will looks like:
SELECT * from EmaployeeTable
where Palindrome = SubString
BUT!
This is not a good idea. Please tell us, why you dont want to use string functios.
You could do it building a list of palindrome words using a recursive query that generates palindrome words till a length o n characters and then selects employees with the name matching a palindrome word. This may be a really inefficient way, but it does the trick
This is a sample query for Oracle, PostgreSQL should support this feature as well with little differences on syntax. I don't know about other RDBMS.
with EmployeeTable AS (
SELECT 'ADA' AS employeename
FROM DUAL
UNION ALL
SELECT 'IDA' AS employeename
FROM DUAL
UNION ALL
SELECT 'JACK' AS employeename
FROM DUAL
), letters as (
select chr(ascii('A') + rownum - 1) as letter
from dual
connect by ascii('A') + rownum - 1 <= ascii('Z')
), palindromes(word, len ) as (
SELECT WORD, LEN
FROM (
select CAST(NULL AS VARCHAR2(100)) as word, 0 as len
from DUAL
union all
select letter as word, 1 as len
from letters
)
union all
select l.letter||p.word||l.letter AS WORD, len + 1 AS LEN
from palindromes p
cross join letters l
where len <= 4
)
SEARCH BREADTH FIRST BY word SET order1
CYCLE word SET is_cycle TO 'Y' DEFAULT 'N'
select *
from EmployeeTable
WHERE employeename IN (
SELECT WORD
FROM palindromes
)
DECLARE #cPalindrome VARCHAR(100) = 'SUBI NO ONIBUS'
SET #cPalindrome = REPLACE(#cPalindrome, ' ', '')
;WITH tPalindromo (iNo) AS (
SELECT 1
WHERE SUBSTRING(#cPalindrome, 1, 1) = SUBSTRING(#cPalindrome, LEN(#cPalindrome), 1)
UNION ALL
SELECT iNo + 1
FROM tPalindromo
WHERE SUBSTRING(#cPalindrome, iNo + 1, 1) = SUBSTRING(#cPalindrome, LEN(#cPalindrome) - iNo, 1)
AND LEN(#cPalindrome) > iNo
)
SELECT IIF(MAX(iNo) = LEN(#cPalindrome), 'PALINDROME', 'NOT PALINDROME')
FROM tPalindromo