SQL: See how many items in a list, return a hit in a table (using LIKE) - sql

For the sake of argument, say I'm looking for first names in a table. And, the table is irregular - no pattern, some people have 2 names, some 3, some it's all run together, whatever.
SELECT
COUNT(*)
FROM
NAME_TABLE N
WHERE
--the list
N.NAME LIKE 'John%'
OR N.NAME LIKE 'Mich%'
OR N.NAME LIKE 'Rob%'
The above would give how many hits - maybe 70 or 70000, who knows. But, what I really want is a response from 0-3.
i.e., how many of my search terms, get a hit in the table.
I could just run the query and pull the entire table of hits, then
use Excel to get the answer.
Or if in a more typical programming language, I could run a loop
that has an x + 1 in it.
But is there a way to do this directly in an SQL query? Specifically T-SQL I guess...very specifically SQL Server 2008, but I'm kinda curious in general.

Your question is not very clear, at least for me, but my magic crystall ball tells me, that eventually you are looking for something like this:
USE master;
GO
SELECT SUM(CASE WHEN PATINDEX('sys%',[name])>0 THEN 1 ELSE 0 END) AS CountOfSys
,SUM(CASE WHEN PATINDEX('plan%',[name])>0 THEN 1 ELSE 0 END) AS CountOfPlan
,SUM(CASE WHEN PATINDEX('spt_%',[name])>0 THEN 1 ELSE 0 END) AS CountOfSpt
FROM sys.objects;
Another approach was to do a
SELECT 'sys%' AS Pattern, COUNT(*) AS CountPattern
FROM ... WHERE [name] LIKE 'sys%'
UNION ALL
SELECT 'plan%',COUNT(*)
...
UNION ALL
...
This would return a list of all your counts in tabular form.
The third chance was to place all your search patterns into a table and use this table in a CROSS JOIN (similar idea then the UNION approach, but more flexible and more generic):
USE master;
GO
DECLARE #tblPattern TABLE(Pattern VARCHAR(100));
INSERT INTO #tblPattern VALUES('sys%'),('plan%'),('spt_%');
SELECT p.Pattern
,SUM(CASE WHEN PATINDEX(p.Pattern,o.[name])>0 THEN 1 ELSE 0 END) AS CountPattern
FROM sys.objects AS o
CROSS JOIN #tblPattern AS p
GROUP BY p.Pattern

You can use a case statement for each name that resolves to 1 if the name exists in the table or 0 if it does not. Then just add them together and the result will be 0-3 i.e. the number of names that exist in the table.
select
case when exists
select 1 from name_table
where name like 'John%'
then 1 else 0 end
+
case when exists
select 1 from name_table
where name like 'Mich%'
then 1 else 0 end
+
case when exists
select 1 from name_table
where name like 'Rob%'
then 1 else 0 end

Related

Using A Count And A Case Statement In One Query

I'm pretty much out of ideas on how to get this to work.I haven't really used SQL in several years so there's a lot I don't remember.
So here is what I would like to happen:
I return the rows where the Code field from table has the value 1208 AND estnumber = 1187216
Run a count on the selection, if 0 run a subquery
If >0 run a different subquery
I didn't get to the subquery part yet because I can't get this to work correctly at all. Right now I just want it to return text.
Here is the latest attempt, I'm actually using db2 but maybe we can ignore that for now and i'll work that part out later because it says the syntax isnt correct, but other validators disagree (if you dont know anything about db2 just use standard sql when giving advice)
SELECT
count(*) AS t
FROM
table
WHERE
(
ESTNUMBER = 1187216
AND CODE = 1208
)
AND CASE WHEN t = 0 THEN 'it is zero' ELSE 'it is not zero' END;
Are you trying to do something like this?
WITH c AS (
SELECT count(*) AS cnt
FROM table
WHERE ESTNUMBER = 1187216 AND CODE = 1208
)
SELECT s1.*
FROM subquery1 s1
WHERE (SELECT cnt FROM c) = 0
UNION ALL
SELECT s2.*
FROM subquery2 s2
WHERE (SELECT cnt FROM c) > 0;
This assumes that the columns returned by the subqueries are compatible (same number, same types).
There are better ways to write this query (notably using EXISTS and NOT EXISTS), but this conforms directly to how you asked the question.
The string value should come up in the select clause and not in the where filter.
SELECT
count(*) AS t,
(CASE WHEN count(*) = 0 THEN 'it is zero' ELSE 'it is not zero' END) display_str
FROM
table
WHERE
(
ESTNUMBER = 1187216
AND CODE = 1208
)
You're thinking like an imperative programmer, not a declarative one. That is, SQL doesn't have sequential execution: it's all or nothing.
So, here's the start, the bit that works:
SELECT count(*) AS t
FROM table
WHERE ESTNUMBER = 1187216 AND CODE = 1208
Now, to check for the value of count(*), you by now know that WHERE isn't going to work. That's because COUNT is an aggregate function. To look at the result of such of function, you use HAVING.
For your CASE to work, you can move it up into the area that can get count(*) results:
SELECT count(*) AS t
(CASE WHEN count(*) = 0 THEN 'it is zero' ELSE 'it is not zero' END) as msg
FROM table
WHERE ESTNUMBER = 1187216 AND CODE = 1208
Note that "t" is an alias you've given the result of count(*). In most SQL implementations, that alias can't be leveraged in the rest of the statement.
Now, for the either or kind of thing, it would be time to reconsider your approach and what you're really after. You'll probably ultimately have both result sets in your statement and choose how the results are served up.
Something like:
select a.id, a.ct, (case when a.ct=0 then b.amt else c.amt end) as amt
from (select id, count(*) as ct from table1) a
left join (select id, sum(amount) as amt from table2) b on a.id=b.id
left join (select id, sum(amount) as amt from table3) c on a.id=c.id
Hope this helps.

Anyway to use IN operator in the SELECT statement? If not, why?

This may come off as a feature request more than anything, but it would be nice if SQL allowed use of the IN operator in a select statement such as the one below. I want to create new_variable intable1based on the ID variable in table2, hence the case statement.
select ID,
case when ID in (select ID
from table2)
then 1
else 0
end as new_variable
from table1
I understand that SQL will give me an error if I run this, but why is that the case? It doesn't seem obvious to me why SQL developers couldn't enable the IN operator to be used outside of the WHERE clause.
Side note: I'm currently using a left join to avoid this issue, so I am not hung up on this.
select ID,
case when ifnull(b.ID, 0) = 0 then 0
else 1
end as variable_name
from table1
left join(select ID from table2) as b
on a.ID = b.ID
SQL definitely supports this:
select ID,
(case when ID in (select ID from table2)
then 1 else 0
end) as new_variable
from table1
Note that there is a comma after id.
This is standard SQL. If your database doesn't support it, it is a feature request (and one that all or almost all databases support).

Alternative to executing Netezza SQL subquery multiple times in Case When?

SELECT DISTINCT lr.id,
lr.dept,
lr.name
Case When lr.id IN (SELECT id FROM RESULTS WHERE PANEL_FLAG LIKE '%value1%') AND lr.id IN (SELECT id FROM RESULTS WHERE PANEL_FLAG LIKE '%value2%') Then 1
Else 0
End As both_panels,
Case When lr.id IN (SELECT id FROM RESULTS WHERE PANEL_FLAG LIKE '%value1%') AND lr.id NOT IN (SELECT id FROM RESULTS WHERE PANEL_FLAG LIKE '%value2%') Then 1
Else 0
End As only_value1_panel,
FROM RESULTS lr
I have simplified this, in reality I actually need many more Case When statements and it's performance nightmare because the subquery executes each time. Is there a more performant way to do this?
I tried creating Common Table Expressions and Temp Tables before the query, but the way I was doing it (replacing the subquery statements with a SELECT from the CTE or the Temp Table) doesn't seem to make any performance difference as it is still executing a query each time.
This would usually be handled with conditional aggregation. I think this captures your logic:
SELECT lr.id, lr.dept, lr.name,
LEAST(MAX(Case When PANEL_FLAG LIKE '%value1%' THEN 1 ELSE 0 END),
MAX(Case When PANEL_FLAG LIKE '%value2%' THEN 1 ELSE 0 END)
) As both_panels,
LEAST(MAX(Case When PANEL_FLAG LIKE '%value1%' THEN 1 ELSE 0 END),
MAX(Case When PANEL_FLAG LIKE '%value2%' THEN 0 ELSE 1 END)
) as only_value1_panel,
FROM RESULTS lr
GROUP BY lr.id, lr.dept, lr.name
If the sub queries for the ‘in’ lists are with a ‘constant’ whereclause I would consider populating them in a script (commas and all), then inject them into a ‘sql template’ file and run it.
That will run very fast.
Of course the resulting lists should be fairly small (less than 60KB for all lists in total) otherwise the sql statement will become too large.

Replacing In clause with exists

HI Gurus,
I'm looking to replace an IN clause with exists, but despite reading other similar cases on here I've not been able to apply them to my dataset.
I am looking to add in a column to my main query which tells me if a fund is found within a separate list, and if it does then label it 'emergency' and if not then 'non-emergency'
The list is defined like so:
select
f.id
FROM _audit a
INNER JOIN _fund f ON a.article_id = f.id
WHERE a.entity_name = 'Fund'
AND a.Changes LIKE
'%finance_code2%OldValue>3%'
)
UNION
(
select
id AS fund_reference
FROM _fund
WHERE (finance_code2 LIKE '3%'
OR finance_code2 LIKE '9%')
AND finance_code2 IS NOT NULL
And so what I am looking for is essentially something like:
SELECT
...Main query here...
,CASE WHEN fund_id IN (list_details) THEN 'emergency' else 'non-emergency' end
I know that it would be more efficient to do something like
SELECT
...Main query here...
,SELECT CASE WHEN EXISTS
(SELECT fund_id FROM list_details WHERE fund_id IS NOT NULL) THEN 'emergency' else 'non-emergency' END
But every time I try it keeps returning false values (saying that funds are contained within the list when they are not)
In case it helps I'm using sql server 2005 and the main query is listed below, where the list_details result (id) is joined onto donation_fund_allocation on list_details.id = donation_fund_allocation.fund_id
As always any clue would be massively appreciated :)
Thanks!
Main query
SELECT
don.supporter_id AS contact_id
,don.id AS gift_id
,YEAR(don.date_received) AS calendar_year
,YEAR(don.date_received) - CASE WHEN MONTH(don.date_received) < 4 THEN 1 ELSE 0 END AS financial_year
,don.date_received AS date_received
,don.event_id AS event_id
,SUM(CASE WHEN don.gift_aid_status <> 4 THEN don.value_gross * ((dfa.percentage) / 100)
WHEN don.gift_aid_status = 4 AND don.value_net > don.value_gross
AND don.value_net <> 0 THEN don.value_net * ((dfa.percentage) / 100)
ELSE don.value_gross * ((dfa.percentage) / 100)
END
) AS donation_value
--**List details query to go in here**
FROM donation don WITH (nolock)
INNER JOIN donation_fund_allocation dfa WITH (nolock) ON dfa.donation_id = don.id
WHERE don.supporter_id IS NOT NULL
AND don.status = 4
AND don.value_gross <> 0
GROUP BY don.supporter_id
,don.id
,don.date_received
,don.event_id
You need to correlate the exists call with the outer query. As written you are just asking if there exist any rows in list_details where fund_id isn't null
So, what you actually want is
SELECT
...Main query here...
,SELECT CASE WHEN EXISTS
(SELECT 1 FROM list_details WHERE fund_id = outer.fund_id) THEN 'emergency' else 'non-emergency' END
Where outer is the table alias for where fund_id can be found in your main select
You could write a function which takes the fund_id and returns an appropriate string value of "emergency" or "non-emergency".

Most optimized way to get column totals in SQL Server 2005+

I am creating some reports for an application to be used by various states. The database has the potential to be very large. I would like to know which way is the best way to get column totals.
Currently I have SQL similar to the following:
SELECT count(case when prg.prefix_id = 1 then iss.id end) +
count(case when prg.prefix_id = 2 then iss.id end) as total,
count(case when prg.prefix_id = 1 then iss.id end) as c1,
count(case when prg.prefix_id = 2 then iss.id end) as c2
FROM dbo.TableName
WHERE ...
As you can see, the columns are in there twice. In one instance, im adding them and showing the total, in the other im just showing the individual values which is required for the report.
This is a very small sample of the SQL, there are 20+ columns and w/i those columns 4 or more of them are being summed at times.
I was thinking of declaring some #Parameters and setting each of the columns equal to a #Parameter, then I could just add up which ever #Parameters I needed to show the column totals, IE: SET #Total = #c1 + #c2
But, does the SQL Server engine even care the columns are in there multiple times like that? Is there a better way of doing this?
Any reason this isn't done as
select prg.prefix_id, count(1) from tablename where... group by prg.prefix_id
It would leave you with a result set of the prefix_id and the count of rows for each prefix_ID...might be preferential over a series of count(case) statements, and I think it should be quicker, but I can't confirm for sure.
I would use a subquery before resorting to #vars myself. Something like this:
select c1,c2,c1+c1 as total from
(SELECT
count(case when prg.prefix_id = 1 then iss.id end) as c1,
count(case when prg.prefix_id = 2 then iss.id end) as c2
FROM dbo.TableName
WHERE ... ) a
Use straight SQL if you can before resorting to T-SQL procedure logic. Rule of thumb if you can do it in SQL do it in SQL. If you want to emulate static values with straight SQL try a inline view like this:
SELECT iv1.c1 + iv1.c2 as total,
iv1.c1,
iv1.c2
FROM
(
SELECT count(case when prg.prefix_id = 1 then iss.id end) as c1,
count(case when prg.prefix_id = 2 then iss.id end) as c2
FROM dbo.TableName
WHERE ...
) AS iv1
This way you logically are getting the counts once and can compute values based on those counts. However I think SQL Server is smart enough to not have to scan for the count n number of times so I don't know that your plan would differ from the SQL I sent and the SQL you have.