Warning: Your questions suggests you are storing passwords as plain text in your database.
This is a major security risk. Passwords should be stored as salted hash, not encrypted and never as plain text (Thanks to Sebastian Brosch for noticing this).
Having said that, here's an answer to your question:
One way to do it is to break the string into single chars, and then simply query with count
:
DECLARE @str nvarchar(30) = 'shS46@($8jr4';
With N10 AS
(
SELECT N
FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9))V(N)
), Tally AS
(
SELECT TOP(LEN(@str)) ROW_NUMBER() OVER(ORDER BY @@SPID) As N
FROM N10 ten
CROSS JOIN N10 hundred
-- Passwords are usually 10-20 chars max length.
-- If you need more you can add another cross join to get 1000.
), Chars AS
(
SELECT SUBSTRING(@str, N, 1) As C
FROM Tally
)
SELECT COUNT(*)
FROM Chars
WHERE C NOT LIKE '%[A-Za-z0-9]%'
Of course, if you already have a tally table you don't need to create a tally cte on the fly:
With Chars AS
(
SELECT SUBSTRING(@str, N, 1) As C
FROM Tally
WHERE N <= LEN(@str)
)
SELECT COUNT(*)
FROM Chars
WHERE C NOT LIKE '%[A-Za-z0-9]%'
And a complete version that uses a Login table with a Password column:
(and another way to populate an on-the-fly tally cte)
CREATE TABLE Login
(
Password nvarchar(20)
);
INSERT INTO Login (Password) VALUES
('n9$%^Usj4jjr'),
('Nehj47$%^$'),
('MNAtokay543^A36#$^#%'),
('(*&^#$^dfh$%&'),
('$%^h345nfs54y');
With Tally AS
(
SELECT TOP 20 ROW_NUMBER() OVER(ORDER BY @@SPID) As N
FROM sys.objects
), Chars AS
(
SELECT Password, SUBSTRING(Password, N, 1) As C
FROM Login
CROSS JOIN Tally
WHERE N <= LEN(Password)
)
SELECT Password
FROM Chars
GROUP BY Password
HAVING COUNT(CASE WHEN C NOT LIKE '%[A-Za-z0-9]%' THEN 1 END) > 5
Results:
Password
(*&^#$^dfh$%&
MNAtokay543^A36#$^#%