Multiple MySQL Nested Selects - mysql

Hi there I wonder if someone could help, I am having a complete mental block.
I have this select statment that works perfectly, however I now want to check the results (FirstName and Surname) against another table to find out more details about them. I can't for the life of me figure how to do that.
SELECT `FirstName` , `Surname` , MobilePhone
FROM users
WHERE NOT
EXISTS (
SELECT PhoneNumber
FROM Orangedata
WHERE users.MobilePhone = orangedata.`PhoneNumber`
)
This select returns me a list of records (Name and Mobiles) that I want to run another select with.
The Result looks like
FirstName Surname Mobile
I want to run my next Select that says
Select *
from Table 3
WHERE FirstName = FirstName from previous select AND WHERE Surname = Surname from previous select.
The overall result I am looking for is
Give me all the details (from Table 3) for a user that does not have any record in table Orangedata
Its effectively three nested selects (I think) and I just can't figure it out

SELECT table3.* FROM
table3
JOIN
(SELECT `FirstName` , `Surname` , MobilePhone
FROM users
WHERE NOT
EXISTS (
SELECT PhoneNumber
FROM Orangedata
WHERE users.MobilePhone = orangedata.`PhoneNumber`
)) as b
ON (table3.FirstName = b.FirstName AND table3.Surname = b.Surname))
Or something like this

Related

Fuzzy match on a left join

I am looking to join two tables where there's a 90% match for example.
Taking the example below, I want to join table A and table B on the phone number. You can see that the phone numbers differ slightly (the international code). I'd like the end result to show as table C.
I imagine it'll be something like but the join would specify to match on 90% of the phone_number
select
a.*,
b.most_recent_booking_date
from a
left join b
on a.phone_number = b.phone_number
Hope that's clear and any help would be great! Cheers!
Table A
Phone number
Most recent call date
441234567891
01/05/22
441234567892
02/05/22
Table B
Phone number
Most recent booking date
+441234567891
03/05/22
+441234567892
04/05/22
Table C
Phone number
Most recent call date
Most recent bookingdate
441234567891
01/05/22
03/05/22
441234567892
02/05/22
04/05/22
You can try something like this, but I don't like it, as Demeteor says you should have an ID to join on. Note that I use a left join here, in case there is no data in table #T2. I was also considering a computed column where it removes the + and then you could join that way too. I will also get told off for SQL injection if the phone number could be dodgy.
CREATE TABLE #T1 (
PhoneNumber VARCHAR(20) NOT NULL,
CallDate DATE NOT NULL
);
CREATE TABLE #T2 (
PhoneNumber VARCHAR(20) NOT NULL,
BookingDate DATE NOT NULL
);
INSERT INTO #T1 (PhoneNumber, CallDate)
VALUES
('441234567891', '20220501'),
('441234567892', '20220502');
INSERT INTO #T2 (PhoneNumber, BookingDate)
VALUES
('+441234567891', '20220503'),
('+441234567892', '20220504');
GO
SELECT *
FROM #T1 AS T1
LEFT JOIN #T2 AS T2 ON T2.PhoneNumber LIKE '%' + T1.PhoneNumber;

SQL update table and INSERT only new lines with selected fields from another table

I am trying to update mysql databases of 2 sites in order to have both the same products, I need to find a query that will add ONLY new product lines to the product table, I am trying with:
INSERT INTO pre_upde_prop
(id
,pro_name
,pro_alias
,agent_id
,ref
,category_id
,price
,pro_small_desc
,pro_full_desc
,pro_type
,isFeatured
,published
,isSold
,note
,city
,state
,country
,province
,postcode
,hits
,created
,modified
,bed_room
,square_feet
,lot_size
,number_of_floors
,parking
,address
)
SELECT id
, name
, alias
, agent_id
, ref
, cid
, price
, description
, text
, type
, featured
, published
, available
, description
, lid
, sid
, cyid
, lid
, postcode
, hits
, listdate
, refresh_time
, bedrooms
, area
, covered_area
, garage
, garage
, years
FROM pre2_properties_products;
but when i run this query it adds duplicates of all lines. I need to find a query that, for example, starts the insert at line id number 6000, for example, inserting only new line after 6000.
Both table have same product ids.
You need some way to restrict the records that are going into via the INSERT. Currently you are SELECTing from a table without any criteria. What you need is to restrict the results of that SELECT statement to eliminate any records that already exist.
Where it would be easy enough to add WHERE ID > 6000 to the end of the query that you've shown, this is of little use for anything more than an ad-hoc update. More stable methods are a) a frustrated join against the table, or b) a WHERE NOT IN/WHERE NOT EXISTS clause:
Method 0:
INSERT INTO pre_upde_prop (...)
SELECT ...
FROM pre2_properties_products
WHERE id > 6000;
Method 1:
INSERT INTO pre_upde_prop (...)
SELECT ...
FROM pre2_properties_products AS a
LEFT JOIN pre_udpe_prop AS b ON b.ID = a.ID
WHERE b.ID IS NULL
Method 2:
INSERT INTO pre_upde_prop (...)
SELECT ...
FROM pre2_properties_products AS a
WHERE id NOT EXISTS (
SELECT id FROM pre_udpe_prop
);

Mysql deduplicate records in single query

I have the following table:
CREATE TABLE `relations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`relationcode` varchar(25) DEFAULT NULL,
`email_address` varchar(100) DEFAULT NULL,
`firstname` varchar(100) DEFAULT NULL,
`latname` varchar(100) DEFAULT NULL,
`last_contact_date` varchar(25) DEFAULT NULL,
PRIMARY KEY (`id`)
)
In this table there are duplicates, these are relation with exact the same relationcode and email_address. They can be in there twice or even 10 times.
I need a query that selects the id's of all records, but excludes the ones that are in there more than once. Of those records, I only would like to select the record with the most recent last_contact_id only.
I'm more into Oracle than Mysql, In Oracle I would be able to do it this way:
select * from (
select row_number () over (partition by relationcode order by to_date(last_contact_date,'dd-mm-yyyy')) rank,
id,
relationcode,
email_address ,
last_contact_date
from RELATIONS)
where rank = 1
But I can't figure out how to modify this query to work in MySql. I'm not even dure it's possible to do the same thing in a single query in MySQl.
Any ideas?
Normal way to do this is a sub query to get the latest record and then join that against the table:-
SELECT id, relationcode, email_address, firstname, latname, last_contact_date
FROM RELATIONS
INNER JOIN
(
SELECT relationcode, email_address, MAX(last_contact_date) AS latest_contact_date
FROM RELATIONS
GROUP BY relationcode, email_address
) Sub1
ON RELATIONS.relationcode = Sub1.relationcode
AND RELATIONS.email_address = Sub1.email_address
AND RELATIONS.last_contact_date = Sub1.latest_contact_date
It is possible to manually generate the kind of rank that your Oracle query uses using variables. Bit messy though!
SELECT id, relationcode, email_address, firstname, latname, last_contact_date
FROM
(
SELECT id, relationcode, email_address, firstname, latname, last_contact_date, #seq:=IF(#relationcode = relationcode AND #email_address = email_address, #seq + 1, 1) AS seq, #relationcode := relationcode, #email_address := email_address
(
SELECT id, relationcode, email_address, firstname, latname, last_contact_date
FROM RELATIONS
CROSS JOIN (SELECT #seq:=0, #relationcode := '', #email_address :='') Sub1
ORDER BY relationcode, email_address, last_contact_date DESC
) Sub2
) Sub3
WHERE seq = 1
This uses a sub query to initialise the variables. The sequence number is added to if the relation code and email address are the same as the previous row, if not they are reset to 1 and stored in a field. Then the outer select check the sequence number (as a field, not as the variable name) and records only returned if it is 1.
Note that I have done this as multiple sub queries. Partly to make it clearer to you, but also to try to force the order that MySQL executes it is. There are a couple of possible issues with how MySQL says it may order the execution of things that could cause an issue. They never have done for me, but with sub queries I would hope for force the order.
Here is a method that will work in both MySQL and Oracle. It rephrases the question as: Get me all rows from relations where the relationcode has no larger last_contact_date.
It works something like this:
select r.*
from relations r
where not exists (select 1
from relations r2
where r2.relationcode = r.relationcode and
r2.last_contact_date > r.last_contact_date
);
With the appropriate indexes, this should be pretty efficient in both databases.
Note: This assumes that last_contact_date is stored as a date not as a string (as in your table example). Storing dates as strings is just a really bad idea and you should fix your data structure

mysql select a different field is a field is null

I have a staff table
id int(11)
names varchar(120)
family_names varchar(100)
nickname varchar(100)
and a table of who is at work
id int(11)
personid int(11)
titleid int(11)
typeid int(11)
at_work datetime
status int(11)
My problem is that I need to produce a report where I check who is at work and display names, family name and title. As people are called by nickname, I need to check first if nickname exist so I find this example on this site
SELECT IF(LENGTH(nickname)>0, nickname, names) FROM staff_list
I will get either the nickname or the names, but it do not solve my request as I need to search mu who_is_at_work table and then get the staff at work and create that list.
Is it possible to do a select within a select to get the three fields that I need
nickname/names, family name, title
After a while I figured out how to do it.
I found the solution, thanks for posting. Here is what I ended up doing.
SELECT p.id AS ID,
IF(LENGTH(p.nickname)>0, p.nickname, p.names) AS 'Names/Nickname',
p.family_names AS 'Family name',
r.rank AS 'Rank'
FROM atworklist AS who
LEFT JOIN stafflist as p on p.id = who.personid
LEFT JOIN title AS r ON r.id = p.titleid
where who.shipid= 2 and who.status in (2,3)
ORDER BY r.sortorder
Works like a charm for me
If you want to get rid of the null values you can use coalesce.
select coalesce(nickname, names) from staff_list
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_coalesce
If you want to check for empty strings you can do it this way:
select case when nickname = '' then names else nickname end from staff_list

Getting multiple values from a query

I write a query to get values from 3 tables but this is returning multiple values so can any one tell where i went wrong
select c.CompanyName,cd.FedTaxID,cd.EmailAddress,cd.PhoneNumber
from tblcustomerdetail cd,tblcustomer c
where c.FedTaxID in (
select FedTaxID
from tblcustomer
where CustomerID in (
select LOginID
from tbluserlogindetail
where UserName like "pa%" and RoleTypeID='20'
)
)
and cd.FedTaxID in (
select FedTaxID
from tblcustomer
where CustomerID in (
select LOginID
from tbluserlogindetail
where UserName like "pa%" and RoleTypeID='20'
)
);
My relation is here
My 3 tables are `tbluserlogindetails, tblcustomerdetails and tblCustomer'
1) Initially i will get `Login ID` from `tblUserLoginDetail ` based on the `user name`.
2) Next based on `LoginID` i will get `FedTaxID` from tblcustomerDetail`
3) Next based on 'FedTaxID' i will get the the required details from `tblcustomer'
SELECT
tblcustomer.CompanyName,
tblcustomerdetail.FedTaxID,
tblcustomerdetail.EmailAddress,
tblcustomerdetail.PhoneNumber
FROM tbluserlogindetail, tblcustomer, tblcustomerdetail
WHERE
tbluserlogindetail.LOginID = tblcustomer.CustomerID
AND tblcustomer.FedTaxID = tblcustomerdetail.FedTaxID
AND tbluserlogindetail.UserName LIKE 'pa%'
AND tbluserlogindetail.RoleTypeID = '20'
Try something like this.
Subqueries have a slow perfomance.
MySQL - SELECT WHERE field IN (subquery) - Extremely slow why?