Using wildcards in HAVING - sql

Im trying to get a list of all the countries that have atleast 2 customers with yahoo email.
So far I came up with this:
SELECT Country
FROM Customer
GROUP BY Country
HAVING COUNT(Email LIKE '%yahoo%')>= 2
This doesnt work though. Probably because I cant use LIKE in HAVING clause. I tried to use it in where clause, but I cant use aggregate functions there. Do you know how to make this work? Assume standard sql. Thx

use conditional aggregation
SELECT Country
FROM Customer
GROUP BY Country
HAVING SUM(case when Email like '%yahoo%' then 1 else 0 end )>= 2
Or just put the limit in your where clause... This way the limit is imposed before the count possibly having a performance boost; since it only has to evaluate the emails that are like '%yahoo%' instead or all emails when counting; but since the where clause has to do the evaluation 1st anyway. I'm not sure which would be faster w/o testing.
SELECT Country
FROM Customer
WHERE email like '%yahoo%'
GROUP BY Country
HAVING count(Email)>= 2
though like %val% is unable to use any index so; maybe not. but if it was like %yahoo.com' you'd see one. (if index is on email)

Use a CTE to build your aggregated list of filtered countries.
SETUP TEST DATA
IF OBJECT_ID('tempdb.dbo.#td', 'U') IS NOT NULL
DROP TABLE #td;
CREATE TABLE #td ( country varchar(10), email varchar(20) ) ;
INSERT INTO #td ( country, email )
VALUES
('US','bob#builder.com')
, ('GB','bob#yahoo.com')
, ('US','bill#yahoo.com')
, ('US','ted#yahoo.com')
, ('FR','joe#friday.com')
, ('GR','jim#gmail.com')
, ('NZ','mrmaori#yahoo.com')
, ('NZ','kiwi#yahoo.com')
, ('US','rufus#yahoo.com')
;
QUERY
WITH CTE AS(
SELECT country, count(*) AS countryCount
FROM #td
WHERE email LIKE '%yahoo%'
GROUP BY country
)
SELECT CTE.country
FROM CTE
WHERE CTE.countryCount >= 2
;
This will give you NZ and US as the only 2 countries. US has 3 yahoo and 1 non-yahoo. NZ has 2 yahoo. GB has only 1 yahoo and is excluded.

Related

How to use LIMIT and IN together to have a default row in SQL?

I am exploring SQL with W3School page and I have this requirements where I need to limit the query to a certain number but also having a default row included with that limit.
Here I want a default row where the customer name is Alfreds, then grab the remaining 29 rows to complete the query regardless of what their name is.
I tried to look on other SO question but they are too complicated to understand and using different syntax.
What you are looking for is a specific order clause.
Try this
SELECT * FROM Customers order by (case when CustomerName in ('Alfreds Futterkiste') then 0 else CustomerId end) limit 30 ;
If you're going to have a default row in SQL you should really have that row in the table with a known primary key, and then UNION it onto your query:
--default row, that is always included as long as the table has a PK 1
SELECT *
FROM Customers
WHERE CustomerId = 1
UNION ALL
--other rows, a variable number of
SELECT *
FROM Customers
WHERE CustomerId <> 1 AND ...
LIMIT 30
The limit presented in this way applies to the result of the Union
If you ever want to do something where you're unioning together limited sets in other combinations you might want to look at eg a form like
(... LIMIT 2)
UNION ALL
(... LIMIT 28)
Use UNION to combine the two queries.
SELECT *
FROM Customers
WHERE CustomerName != 'Alfredo Futterkiste'
LIMIT 9
UNION
SELECT *
FROM Customers
WHERE CustomerName = 'Alfreo Futterkiste'

Oracle - Need advice on multiple counts, on table with large amounts of data

Table with large data, does anyone know how to optimize the count statement?
Eg: table Booking(id, email, mobile,....) (about 30 fields).
Function GetBookingCount(p_email, p_mobile) return number
Select count(id)
from Booking
Where email = p_email
or mobile = p_mobile
Function GetBookingStatus3Count(p_email, p_mobile) return number
Select count(id)
from Booking
Where (email = p_email or mobile = p_mobile)
and status = 3;
Final select:
Select GetBookingCount(email, mobile) as BookingCount
, GetBookingStatus3Count(email, mobile) as BookingStatus3Count
, ...
From Booking
where ....
solution1: Set the field column index what in where clause to count as email column, mobile, status column.
solution2: create a new table with few columns to count.
new table: Booking_Stats(id, email, mobile, status).
Thanks for any suggestion.
select count(*) count_all, count( case when status=3 then 1 else null end ) count_status_3
from Booking
where email = p_email and mobile = p_mobile
//NOTE: Query is written from head, not tested
You would consider creating an index on (email,mobile) or on (email,mobile,status) depending how many lines for given (email,mobile) you get and would you pay the cost of update of the index for status change (if allowed). In case of many updates of status for the same line, you might prefer indexing only (email,mobile) [read/write cost trade off].
Email is likely very discriminant (one value filters out most of the columns). If that is not the case, consider changing order to (mobile,email) if mobile column is better candidate.
It seems likely all those GetBookingBlahBlah() functions are not helpful and in fact quiet injurious to performance.
You haven't posted a complete set of requirements (what is meant by ...?), so it's difficult to be certain, but it seems likely that a solution along these lines would be more performative:
with bk as (
select *
from booking
where email = p_email
or mobile = p_mobile
)
select count(*) as BookingCount
, count(case when bk.status = 3 then 1 end) as BookingStatus3Count
, ...
from bk
The idea is to query the base table once, getting all the data necessary to calculate all the counts, and crunching the aggregates on the smallest result set possible.
An index on booking(email,mobile) might be useful but probably not. A better solution would be to have different queries for each of p_email and p_mobile, with single column indexes supporting each query.
The booking table should have an index on email, mobile and status. You should use this select:
WITH B1 AS
(
SELECT ID,
COUNT(ID) CNT1,
STATUS
FROM BOOKING
WHERE EMAIL = P_EMAIL
AND MOBILE = P_MOBILE
)
SELECT CNT1,
COUNT(ID) CNT2
FROM B1
WHERE STATUS = 3;

Can I get some help me in writing a SQL query for searching multiple words in a table column

I am trying to write a query that searches a column of a table and returns the count with an alias name for multiple words.
select
count(STREET_ADDRESS) AS MELBOURNE
from
CUSTOMERS
where
STREET_ADDRESS like '%MELBOURNE%'
I am trying to do this for multiple cities in one query like Melbourne, Sydney, Auckland etc.
Try This,SELECT COUNT(*) FROM CUSTOMERS WHERE STREET_ADDRESS IN ('MANGERE','AVONDALE')GROUP BY STREET_ADDRESS
Keep in mind that the number of columns in the select is fixed. Maybe i missunderstood but you want to input a list of cities and have the output contain as many columns as input's you gave?
If not (fixed Nb cities):
Suggestion: Add conditions to the query (to be honest, since using wildcards no really perf enhancement, you should think about adding a "city" column otherwise if the table gets too big this query will become very slow)
SELECT
SUM(CASE WHEN STREET_ADDRESS LIKE '%MANGERE%' THEN 1 ELSE 0 END) AS MANGERE_COUNT,
SUM(CASE WHEN STREET_ADDRESS LIKE '%AVONDALE%' THEN 1 ELSE 0 END) AS AVONDALE_COUNT
FROM CUSTOMERS
WHERE
STREET_ADDRESS like '%MANGERE%'
OR
STREET_ADDRESS like '%AVONDALE%'
If that is the case:
You will need to create dynamic SQL to transpose the output.
Hope it helps
Sérgio
Have a derived table where you using a case expression returns the city name from each street_address. Then do GROUP BY on its result to count:
select count(*), city
from
(
select case when upper(STREET_ADDRESS) like '%MELBOURNE%' then 'Melbourne'
when upper(STREET_ADDRESS) like '%SYDNEY%' then 'Sydney'
when upper(STREET_ADDRESS) like '%AUCKLAND%' then 'Auckland'
end as city
from CUSTOMERS
) dt
group by city
Note: the UPPER() parts aren't needed if a case insensitive collation is used.

How do I check if all posts from a joined table has the same value in a column?

I'm building a BI report for a client where there is a 1-n related join involved.
The joined table has a field for employee ID (EmplId).
The query that I've built for this report is supposed to give a 1 in its field "OneEmployee" if all the related posts have the same employee in the EmplId field, null if it's different employees, i.e:
TaskTrans
TaskTransHours > EmplId: 'John'
TaskTransHours > EmplId: 'John'
This should give a 1 in the said field in the query
TaskTrans
TaskTransHours > EmplId: 'John'
TaskTransHours > EmplId: 'George'
This should leave the said field blank
The idea is to create a field where a case function checks this and returns the correct value. But my problem is whereas there is a way to check for this through SQL.
select not count(*) from your_table
where employee_id = GIVEN_ID
and your_field not in ( select min(your_field)
from your_table
where employee_id = GIVEN_ID);
Note: my first idea was to use LIMIT 1 in the inner query, but MYSQL didn't like it, so min it was - the points to use any, but only one. Min should work, but the field should be indexed, then this query will actually execute rather fast, as only indexes would be used (obviously employee_id should also be indexed).
Note2: Do not get too confused with not in front of count(*), you want 1 when there is none that is different, I count different ones, and then give you the not count(*), which will be one if count is 0, otherwise 0.
Seems a job for a window COUNT():
SELECT
…,
CASE COUNT(DISTINCT TaskTransHours.EmplId) OVER () WHEN 1 THEN 1 END
AS OneEmployee
FROM …

SQL Order By list of strings?

I wish to do a select on a table and order the results by a certain keyword or list of keywords. For example I have a table like so:
ID Code
1 Health
2 Freeze
3 Phone
4 Phone
5 Health
6 Hot
so rather than just do a simple Order By asc/desc I'd like to order by Health, Phone, Freeze, Hot. Is this possible?
Try using this:
select * from table
order by FIELD(Code, 'Health', 'Phone', 'Freeze', 'Hot')
Here's a horrible hack:
select * from table
order by (
case Code
when 'Health' then 0
when 'Phone' then 1
when 'Freeze' then 2
when 'Hot' then 3
end
)
You can join with the Keywords table, and include a sequence column, and ORDER BY Keyword.Sequence.
Example your keywords table looks like this:
ID Code Sequence
1 Health 1
2 Freeze 3
3 Phone 2
4 Hot 4
Then you can join.
SELECT *
FROM MyTable INNER JOIN
Keywords ON Keywords.ID = MyTable.KeywordID
ORDER BY Keywords.Sequence
Hope this gives you the idea.
Nowadays MySQL has a function called find_in_set()
Use it like this:
select * from table
order by find_in_set(Code,'Health','Phone','Freeze','Hot')
Is this just a one off ORDER BY or something that you're going to want to do often and on more values than specified here?
The order that you have given is arbitrary, therefore an identifier needs to be given to achieve what you want
SELECT
ID,
Code,
CASE Code
WHEN 'Health' THEN 1
WHEN 'Phone' THEN 2
WHEN 'Freeze' THEN 3
WHEN 'Hot' THEN 4
END As OrderBy
FROM Table
ORDER BY
OrderBy
Or
SELECT
ID,
Code
FROM Table
ORDER BY
CASE Code
WHEN 'Health' THEN 1
WHEN 'Phone' THEN 2
WHEN 'Freeze' THEN 3
WHEN 'Hot' THEN 4
END
(I'm not familiar with MySQL but the above would work in SQL Server. The syntax for MySQL won't be too different)
If you're likely to want to do this often, then create an OrderBy column on the table or create an OrderBy table with a FK link to this table and specify an OrderBy numerical field in that.
Hi this is a SQL Server query but I am sure you can do this in MySQL as well:
SELECT ID, Code
FROM x
ORDER BY
CASE Code WHEN 'Health' THEN 1
WHEN 'Phone' THEN 2
WHEN 'Freeze' THEN 4
WHEN 'Hot' THEN 5
ELSE 6 END ASC
, Code ASC
Couple options:
Add OrderCode column with numerical
desired order
Add a table with FK to this table ID
and OrderCode
Yes join your results to your code table and then order by code.CodeOrder
EDIT: Explaing the use of the code table...
Create a separate table of Codes (CodeId, Code, CodeOrder) and join to this and order by CodeOrder. This is nicer than doing the order by (case...) hack suggested since you can easily change the codes and the orders.