Try something like this:
WITH AreaCode (A) AS (
SELECT '[0-9][0-9][0-9][-.]'
UNION ALL SELECT '([0-9][0-9][0-9])-'
), Prefix (P) AS (
SELECT '[0-9][0-9][0-9]-'
), Last4 (L) AS (
SELECT '[0-9][0-9][0-9][0-9]'
), Ext1 (E1) AS (
SELECT ' x'
UNION ALL SELECT ' Ext.'
UNION ALL SELECT ' ext'
), Ext2 (E2) AS (
UNION ALL SELECT '[0-9][0-9]'
UNION ALL SELECT '[0-9][0-9][0-9]'
UNION ALL SELECT '[0-9][0-9][0-9][0-9]'
), Extension (E) AS (
SELECT ''
UNION ALL SELECT E1 + E2 FROM Ext1 CROSS JOIN Ext2
),
SELECT *
FROM
YourTable Y
WHERE NOT EXISTS (
SELECT *
FROM
AreaCode
CROSS JOIN Prefix
CROSS JOIN Last4
CROSS JOIN Extension
WHERE
Y.PhoneNumber LIKE AreaCode + Prefix + Last4 + Extension
);
If you find patterns that are valid but not covered by the query, add them to the parts and pieces shown. If you find something that needs to be together in the two parts, then model it after the Extension CTE (which is either missing or a combination of Ext1 and Ext2). If you need to support international numbers, and they have different patterns (not matching the U.S. 3-3-4) then you'll need some analysis and proper correlating to make the right country codes match up with the right patterns. For example, I know that in certain parts of Brazil, this is a valid number: +55 85 1234-5678 (country code 55, area code two digits, then 4-4 pattern).
Another technique to help you analyze your data is this:
WITH Patterns (P) AS (
SELECT
Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(
PhoneNumber,
'1', '0'), '2', '0'), '3', '0'), '4', '0'),
'5', '0'), '6', '0'), '7', '0'), '8', '0'), '9', '0'
)
)
SELECT P, Count(*)
FROM Patterns
GROUP BY P;
This can help you understand what your data is like by ignoring the actual phone number differences between each row and paying attention only to the arrangement and count of digits. If there are a lot of alpha characters, try to start replacing valid patterns (such as "ext") with a value not found in the list, so you can collapse the rest of the spurious input into something that can be analyzed with a similar Replace() for each letter in the alphabet.