SQL - Query Basic between 2 tables - sql

I would like to know in MYSQL (editor HeidiSQL)
I have 2 Tables (email & spam) 2 rows (ID, EMAIL) and i want to clean my database :
Tables Used in Set Operation Examples
EMAIL
x y
------------------
1 one#edu.com
2 two#edu.com
2 two#edu.com
3 three#edu.com
SPAM
x z
------------------
1 one#edu.com
2 two#edu.com
4 four#edu.com
USUALLY i use EXCEPT but it doesn't work on MYSQL.
proc sql;
title 'EMAIL EXCEPT SPAM'
select * from sql.EMAIL
except
select * from sql.SPAM;
Producing Rows That Are in Only the First Query Result (EXCEPT)
EMAIL EXCEPT SPAM
x y
------------------
3 three#edu.com
i try to use WHERE NOT EXISTS but i don't know.
Can you help me for making the query :
SELECT *
FROM EMAIL
WHERE ... ????
Thank's

SELECT * FROM EMAIL WHERE y NOT IN (SELECT z FROM SPAM)

Left Outer Join with Null in spam should work.
SELECT E.*
FROm EMAIL E
LEFT JOIN SPAM S
ON E.Email = S.Email
WHERE S.Id IS NULL

USUALLY i use EXCEPT but it doesn't work on MYSQL.
EXCEPT is not supported in mySQL. Note that in absence of an explicit ALL | DISTINCT keyword, EXCEPT defaults to EXCEPT DISTINCT. Therefore, in your workaround you should explicitly use SELECT DISTINCT (because SELECT defaults to SELECT ALL).
i try to use WHERE NOT EXISTS
SELECT DISTINCT y
FROM EMAIL
WHERE NOT EXISTS ( SELECT *
FROM SPAM
WHERE y = z );

Related

SQL - to find the most complete strings in a column from a table

Each time a user searches for a text on the website, the search text gets recorded to search_table. The sub-searches are also recorded. They are recorded with an asterisk.
The goal is to find the most complete search texts that the user searched for.
The ideal way would be:
Group the ids = 1,4,6 and obtain id=6
Group the ids = 2,5,7 and obtain id = 7
Group the ids = 3 and obtain id = 3
Group the ids 8, 9 and obtain id = 9
SEARCH_TABLE
id user search_text
--------------------
1 user1 data manag*
2 user1 confer*
3 user1 incomplete sear*
4 user1 data managem*
5 user1 conference c*
6 user1 data management
7 user1 conference call
8 user1 status in*
9 user1 status information
Output should be
user search_text
---------------------
user1 data management
user1 conference call
user1 incomplete sear*
user1 status information
Can you help please?
Something like below should do the work:
SELECT * FROM
SEARCH_TABLE st
WHERE
NOT EXISTS (
SELECT 1 FROM
SEARCH_TABLE st2
-- remove asterkis and ad %
WHERE st2.search_Text LIKE replace(st.search_text,'*','')||'%'
)
This is filtering all searches that are part of others.
This is probably not the most elegant way, but here's a go at it:
alter table your_table
add group_id int
select [user], left(search_text, 5) as Group_Text, IDENTITY(int, 1,1) as Group_ID
into #group_id_table
from your_table
group by [user], left(search_text, 5)
order by [user], left(search_text, 5)
update a
set a.group_id = b.group_id
from your_table as a
join #group_id_table as b
on left(search_text, 5) = group_text
select [user], max(search_text), group_id
from your_table
group by [user], group_id
order by [user], group_id
This achieved the desired results when I ran it, but of course because you're basing the group_id's off a user specified string length there could be issues there. I hope this does the job for you.
Give this a shot. I separated out the completed texts (and their shorter partials), and then found the longest partial for each record. Tested in Oracle as I don't have access to a PostgreSQL right now, but I didn't use anything exotic so it should work.
with
--Contains all completed searches
COMPLETE as (select * from SEARCH_TABLE where SEARCH_TEXT not like '%*'),
--Contains all searches that are incomplete and dont have a completed match
INCOMPLETE as (
select S.*
from SEARCH_TABLE S
left join COMPLETE C
on S.USR = C.USR
and C.SEARCH_TEXT like replace(S.SEARCH_TEXT, '*', '%')
where C.ID is null
),
--chains all incompleted with any matching pattern shorter than it.
CHAINED_INC as (
select LONGER.USR, LONGER.ID, LONGER.SEARCH_TEXT, SHORTER.SEARCH_TEXT SEARCH_TEXT_SHORT
from INCOMPLETE LONGER
join INCOMPLETE SHORTER
on LONGER.SEARCH_TEXT like replace(SHORTER.SEARCH_TEXT, '*', '%')
and LONGER.ID <> SHORTER.ID
)
--if a text is not the shorter text for a different record, that means it's the longest text for that pattern.
select distinct T1.USR, T1.SEARCH_TEXT
from CHAINED_INC T1
left join CHAINED_INC T2
on T1.USR = T2.USR
and T1.SEARCH_TEXT = T2.SEARCH_TEXT_SHORT
where T2.SEARCH_TEXT_SHORT is null
--finally, union back to the completed texts.
union all
select USR, SEARCH_TEXT from COMPLETE
;
Edit: removed ID from select

Passing in a list of ids to several queries

I have serveral queries that use the same ids in a where clause. Currently I have
query 1
select * from X ....... where id in (1,2,3);
query 2
select * from Y ....... where id in (1,2,3);
etc etc
What I would like to have is
query 1
DEFINE VAR LIST;
VAR = (1,2,3)
select * from X ....... where id in (MY_VAR);
select * from Y ....... where id in (MY_VAR);
This will mean if I change ids I wont have to update in two places.
I tried the above but it errors. What's up with my syntax?
Thanks in advance
In SQL Plus, SQL Developer etc.:
SQL> define list = 1,2,3
SQL> select * from x where id in (&list.);
ID
----------
1
2
3
You might try the following:
WITH list_values AS (
SELECT TO_NUMBER(TRIM(REGEXP_SUBSTR('1,2,3', '[^,]+', 1, LEVEL))) AS id
FROM dual
CONNECT BY INSTR('1,2,3', ',', 1, LEVEL - 1) > 0
) SELECT x.* FROM x, list_values
WHERE x.id = list_values.id;
It might be worthwhile to take the subquery above and use it to create a temporary table, then simply use that table moving forward. Among other things it would allow you to have more than 1000 ids in your list.

SQL "IN" statement for multiple columns

I would like to filter Name,X combinations for which is never X=Y
Let's assume the following table:
*Name* *X* *Y*
A 2 1
A 2 2 <--- fulfills requirement for Name=A, X=2
A 10 1
A 10 2
B 3 1
B 3 3 <--- fulfills requirement for Name=B, X=3
B 1 1 <--- fulfills requirement for Name=B, X=1
B 1 3
So I would like to return the combination Name=A, X=10 for which X=Y is never true.
This was my approach (which is syntactically incorrect)
SELECT *
FROM TABLE
WHERE NAME
, X NOT IN (SELECT DISTINCT NAME
, X
FROM TABLE
WHERE X=Y)
My problem is the where statement which cannot handle multiple columns. Does anyone know how to do this?
Just put the columns into parentheses
SELECT *
FROM TABLE
WHERE (NAME, X) NOT IN (SELECT NAME, X
FROM TABLE WHERE X=Y);
The above is ANSI standard SQL but not all DBMS support this syntax though.
A distinct is not necessary for a sub-query for IN or NOT IN.
However NOT EXISTS with a co-related sub-query is very often faster that an NOT IN condition.
I use this on SQL Server
SELECT *
FROM TABLE
WHERE (SELECT NAME + ';' + X)
NOT IN (SELECT NAME + ';' + X
FROM TABLE WHERE X = Y);
I think you can use two condition to achieve this
SELECT *
FROM TABLE
WHERE NAME NOT IN(
SELECT a.NAME FROM TABLE a WHERE a.X=a.Y
) AND X NOT IN (
SELECT b.X FROM TABLE b WHERE b.X=b.Y
)
SELECT *
FROM TABLE T
WHERE NOT EXISTS (SELECT NAME
,X
FROM TABLE t2
WHERE t1.Name=t2.Name
AND t1.X=t2.Y)
This will check if there is such a record

How do I count unique items in field in Access query?

My Table: table1
ID Name Family
1 A AA
2 B BB
3 A AB
4 D DD
5 E EE
6 A AC
SQL command on Access:
select count(*) from table1
Output: ------------> True
6 row(s)
I tried to count unique names:
Expected output: 4 row(s)
select count(distinct Name) from table1
Output on Access: ------------> Error
What changes do I need to make to my query?
Try this
SELECT Count(*) AS N
FROM
(SELECT DISTINCT Name FROM table1) AS T;
Read this for more info.
Access-Engine does not support
SELECT count(DISTINCT....) FROM ...
You have to do it like this:
SELECT count(*)
FROM
(SELECT DISTINCT Name FROM table1)
Its a little workaround... you're counting a DISTINCT selection.
A quick trick to use for me is using the find duplicates query SQL and changing 1 to 0 in Having expression. Like this:
SELECT COUNT([UniqueField]) AS DistinctCNT FROM
(
SELECT First([FieldName]) AS [UniqueField]
FROM TableName
GROUP BY [FieldName]
HAVING (((Count([FieldName]))>0))
);
Hope this helps, not the best way I am sure, and Access should have had this built in.

SQL - Removing Duplicate without 'hard' coding?

Heres my scenario.
I have a table with 3 rows I want to return within a stored procedure, rows are email, name and id. id must = 3 or 4 and email must only be per user as some have multiple entries.
I have a Select statement as follows
SELECT
DISTINCT email,
name,
id
from table
where
id = 3
or id = 4
Ok fairly simple but there are some users whose have entries that are both 3 and 4 so they appear twice, if they appear twice I want only those with ids of 4 remaining. I'll give another example below as its hard to explain.
Table -
Email Name Id
jimmy#domain.com jimmy 4
brian#domain.com brian 4
kevin#domain.com kevin 3
jimmy#domain.com jimmy 3
So in the above scenario I would want to ignore the jimmy with the id of 3, any way of doing this without hard coding?
Thanks
SELECT
email,
name,
max(id)
from table
where
id in( 3, 4 )
group by email, name
Is this what you want to achieve?
SELECT Email, Name, MAX(Id) FROM Table WHERE Id IN (3, 4) GROUP BY Email;
Sometimes using Having Count(*) > 1 may be useful to find duplicated records.
select * from table group by Email having count(*) > 1
or
select * from table group by Email having count(*) > 1 and id > 3.
The solution provided before with the select MAX(ID) from table sounds good for this case.
This maybe an alternative solution.
What RDMS are you using? This will return only one "Jimmy", using RANK():
SELECT A.email, A.name,A.id
FROM SO_Table A
INNER JOIN(
SELECT
email, name,id,RANK() OVER (Partition BY name ORDER BY ID DESC) AS COUNTER
FROM SO_Table B
) X ON X.ID = A.ID AND X.NAME = A.NAME
WHERE X.COUNTER = 1
Returns:
email name id
------------------------------
jimmy#domain.com jimmy 4
brian#domain.com brian 4
kevin#domain.com kevin 3