Combining three SQL queries into one - mysql

I got working code from three queries but I would like to combine them into one or two. Basically I am checking if a provided phone number exists in table contacts or leads as well as if it exists as a secondary number in customfieldsvalues (not all leads have a customfield value though). I am using a CRM system based on CodeIgniter.
What I want to do (non-correct/hypothetical query):
SELECT * FROM contacts OR leads WHERE phonenumber = replace(X, '-', '')
OR leads.id = customvaluefields.relid AND cfields.fieldid = 41 AND cfields.value = X
Tables
table : contacts
+-------+----------------+----------------+
| id | firstname | phonenumber |
+-------+----------------+----------------+
| 1 | John | 214-444-1234 |
| 2 | Mary | 555-111-1234 |
+-------+----------------+----------------+
table : leads
+-------+-----------+---------------------+
| id | name | phonenumber |
+-------+-----------+---------------------+
| 1 | John | 214-444-1234 |
| 2 | Mary | 555-111-1234 |
+-------+-----------+---------------------+
table : customvaluefields
+-------+-----------+-------------+-----------+
| id | relid | fieldid | value |
+-------+-----------+-------------+-----------+
| 1 | 1 | 41 | 222333444 |
| 2 | 1 | 20 | Management|
| 3 | 2 | 41 | 333444555 |
+-------+-----------+-------------+-----------+

If I understand what you are trying to, maybe UNION ALL would work. This is something to get you started:
SELECT C.ID, C.FirstName, C.Phonenumber
FROM Contacts C
JOIN CustomValueField CVF
ON c.ID = CVF.RelID AND
CVF.ID = 41
AND REPLACE(Phonenumber,'-','') = cvf.Value
UNION ALL
SELECT L.ID, L.FirstName, L.Phonenumber
FROM Leads L
JOIN CustomValueField CVF
ON L.ID = CVF.RelID AND
CVF.ID = 41
AND REPLACE(Phonenumber,'-','') = cvf.Value
I'm joining the contacts and leads tables to CustomeValueField in each query and then UNION them together along with the WHERE clause in each. I'm sure it's not 100% correct for what you need, but should get you headed to a solution. Here is more information: https://dev.mysql.com/doc/refman/8.0/en/union.html

Related

Get shared contacts for two users from multiple MySQL tables

I have two tables in my MySQL database.
The first one stores a list of users, and the other, stores a list of contacts for each user.
Users table:
+----+----------+--------------+
| id | name | phoneNumber |
+----+----------+--------------+
| 1 | David | 661-618-5436 |
| 2 | Sarah | 818-526-4830 |
| 3 | Suzan | 323-623-3493 |
+----+----------+--------------+
Contacts table:
+----+-----------------+--------+--------------+
| id | belongsToUserId | name | phoneNumber |
+----+-----------------+--------+--------------+
| 1 | 1 | Gerard | +18185329384 |
| 2 | 1 | Austin | +18739283847 |
| 3 | 2 | Jamie | +15655468907 |
| 4 | 2 | Jade | +19893828192 |
| 5 | 3 | Phil | +18786754234 |
| 6 | 3 | Duke | +18765467832 |
| 7 | 3 | Gerard | +18185329384 |
| 8 | 3 | Jade | +19893828192 |
+----+-----------------+--------+--------------+
What I want to do, is create a query that efficiently takes 2 user IDs and returns the common contacts by phoneNumber for these two users.
For example: User IDs 1 & 3 both have Gerard | +18185329384 in their contacts so the query will return only him.
What could be the most efficient query for this kind of task?
Thanks :)
If I understood your question correctly, I think this might be what you're looking for:
SELECT
c1.id,
c1.belongsToUserId,
c1.phoneNumber,
c1.name
FROM
Contacts c1
JOIN
Contacts c2 ON (c1.phoneNumber=c2.phoneNumber AND c2.userId=3)
WHERE
c1.belongsToUserId =1
You want a self-join:
select c1.name, c1.phonenumber
from contacts c1 join
contacts c2
on c1.name = c2.name and c1.phonenumber = c2.phonenumber and
c1.belongsToUserId = 1 and
c2.belongsToUserId = 3;
The following SELECT will give you all contacts who has more than 1 User.
SELECT Contacts.name, Contacts.phoneNumber
FROM Users INNER JOIN Contacts ON (Users.id = Contacts.belongsToUserId)
GROUP BY Contacts.name, Contacts.phoneNumber
HAVING COUNT (*) > 1
Following query will display shared contact along with the UseID who's having common contacts.
If you just want to show details of shared contacts, then you can skip first 2 columns.
select min(belongsToUserId) as User1,
max(belongsToUserId) as User2,
name,
phone Number
from contacts
group by name,phoneNumber
having count(*) > 1;
SELECT
cp.name,
cs.phoneNumber
FROM
Contacts cp
INNER JOIN Contacts cs ON
cp.name = cs.name AND cp.phoneNumber = cs.phoneNumber
AND
cp.belongsToUserId IN ( 1,3 );

MySQL Performance - LEFT JOIN / HAVING vs Sub Query

Which of the following queries style is better for performance?
Basically, I'm returning many related records into one row with GROUP_CONCAT and I need to filter by another join on the GROUP_CONCAT value, and I will need to add many more either joins/group_concats/havings or sub queries in order to filter by more related values. I saw that, officially, LEFT JOIN was faster, but I wonder if the GROUP_CONCAT and HAVING through that off.
(This is a very simplified example, the actual data has many more attributes and it's reading from a Drupal MySQL architecture)
Thanks!
Main Records
+----+-----------------+----------------+-----------+-----------+
| id | other_record_id | value | type | attribute |
+----+-----------------+----------------+-----------+-----------+
| 1 | 0 | Red Building | building | |
| 2 | 1 | ACME Plumbing | attribute | company |
| 3 | 1 | east_side | attribute | location |
| 4 | 0 | Green Building | building | |
| 5 | 4 | AJAX Heating | attribute | company |
| 6 | 4 | west_side | attribute | location |
| 7 | 0 | Blue Building | building | |
| 8 | 7 | ZZZ Mattresses | attribute | company |
| 9 | 7 | south_side | attribute | location |
+----+-----------------+----------------+-----------+-----------+
location_transaltions
+-------------+------------+
| location_id | value |
+-------------+------------+
| 1 | east_side |
| 2 | west_side |
| 3 | south_side |
+-------------+------------+
locations
+----+--------------------+
| id | name |
+----+--------------------+
| 1 | Arts District |
| 2 | Warehouse District |
| 3 | Suburb |
+----+--------------------+
Query #1
SELECT
a.id,
GROUP_CONCAT(
IF(b.attribute = 'company', b.value, NULL)
) AS company_value,
GROUP_CONCAT(
IF(b.attribute = 'location', b.value, NULL)
) AS location_value,
GROUP_CONCAT(
IF(b.attribute = 'location', lt.location_id, NULL)
) AS location_id
FROM
records a
LEFT JOIN records b ON b.other_record_id = a.id AND b.type = 'attribute'
LEFT JOIN location_translations lt ON lt.value = b.value
WHERE a.type = 'building'
GROUP BY a.id
HAVING location_id = 2
Query #2
SELECT temp.* FROM (
SELECT
a.id,
GROUP_CONCAT(
IF(b.attribute = 'company', b.value, NULL)
) AS company_value,
GROUP_CONCAT(
IF(b.attribute = 'location', b.value, NULL)
) AS location_value
FROM
records a
LEFT JOIN records b ON b.other_record_id = a.id AND b.type = 'attribute'
WHERE a.type = 'building'
GROUP BY a.id
) as temp
LEFT JOIN location_translations lt ON lt.value = temp.location_value
WHERE location_id = 2
Using JOIN is preferable in most cases, because it helps optimizer to understand which indexes he can to use. In your case, query #1 looks good enough.
Of course, it works only if tables has indexes. Check table records has indexes on id, other_record_id, value and type columns, table location_translations on value

select rows where related record doesn't exist

I need to retrieve rows from a mysql database as follows: I have a contract table, a contract line item table, and another table called udac. I need all contracts which DO NOT have a line item record with criteria based on a relationship between contract line item and udac. If there is a better way to state this question, let me know.
Table Structures
----contract--------------------- ---contractlineitem-----------
| id | customer_id | entry_date | | id | contract_id | udac_id |
--------------------------------- ------------------------------
| 1 | 1234 | 2010-01-01 | | 1 | 1 | 5 |
| 2 | 2345 | 2016-01-31 | | 2 | 1 | 2 |
--------------------------------- | 3 | 1 | 1 |
| 4 | 2 | 4 |
| 5 | 2 | 2 |
------------------------------
---udac----------
| id | udaccode |
-----------------
| 1 | SWBL/R |
| 2 | SWBL |
| 3 | ABL/R |
| 4 | ABL |
| 5 | XRS/F |
-----------------
Given the above data, contract 2 would show up but contract 1 would not, because it has contractlineitems that point to udacs that end in /F or /R.
Here's what i have so far, but it's not correct.
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND NOT EXISTS (
SELECT cli.id
FROM contractlineitem cli_i
JOIN udac u
ON cli_i.udac_id = u.id
WHERE u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R'
AND cli_i.contract_id = cli.contract_id);
Tom's comment that your WHERE clause is wrong may be the problem you are chasing. Plus, using a correlated subquery may be problematic for performance if the optimizer can't figure out a better way to do it.
Here is the better way to do it using an OUTER JOIN:
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
LEFT OUTER JOIN udac u
ON ( u.id = cli.udac_id
AND ( u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R' ) )
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND u.id IS NULL
Try that out and see if it does what you want. The query essentially does what you stated: It tries to join to udac where the code ends in '/F' or '/R', but then it only accepts the ones where it can't find a match (u.id IS NULL).
If the same row is returned multiple times incorrectly, throw a distinct on the front.

How to join tables with SQL query and take number of tied columns?

I'm having BookTable in database (with foregin hey LibID):
| BookID | BookName | BookPrice | LibID |
-------------------------------------------
| 1 | Book_1 | 200 | 1 |
| 2 | Book_2 | 100 | 1 |
| 3 | Book_3 | 300 | 2 |
| 4 | Book_4 | 150 | 4 |
and also LibraryTable:
| LibID | LibName | LibLocation |
-----------------------------------
| 1 | Lib_1 | Loc_1 |
| 2 | Lib_2 | Loc_2 |
| 3 | Lib_3 | Loc_3 |
| 4 | Lib_4 | Loc_4 |
I need to write SQL query that will return be the info about the library and number of books for that library:
| LibID | LibName | NumberOfBooks|
------------------------------------
| 1 | Lib_1 | 2 |
| 2 | Lib_2 | 1 |
| 3 | Lib_3 | 0 |
| 4 | Lib_4 | 1 |
It should be one SQL query, probably with nested queries or joins.. Not sure how the query should look like:
SELECT L.LibID AS LibID, L.LibName AS LibName, COUNT(B) AS NumberOfBooks
FROM LibraryTable L, BookTable B
WHERE L.LibID = B.LibID
Will that work?
No, this query will not work. COUNT aggregates data, so you must explicitely tell the DBMS for which group of data you want the count. In your case this is the library (you want one result record per library).
COUNT's parameter is a column, not a table, so change this to * (i.e. count records) or a certain column (e.g. LibID).
The join syntax you are using is valid, but deprecated. Use explicit joins instead. In your case an outer join would even show libraries that have no books at all, if such is possible.
select l.libid, l.libname, count(b.libid) as numberofbooks
from librarytable l
left outer join booktable b on b.libid = l.libid
group by l.libid;
You could also do all this without a join at all and get the book count in a subquery instead. Then you wouldn't have to aggregate. That's way simpler and more readable in my opinion.
select
l.libid,
l.libname,
(select count(*) booktable b where b.libid = l.libid) as numberofbooks
from librarytable l;
SELECT lt.LibID AS LibID, lt.LibName AS LibName, count(*) AS NumberOfBooks
FROM BookTable AS bt
LEFT JOIN LibraryTable AS lt ON bt.LibID = lt.LibID
GROUP BY bt.LibID

MySQL Join and Default

I'm still very new to SQL queries and can't quite figure this one out.
I have two tables, one table I'm running a general SELECT ... WHERE, super easy SQL statement.
Ex:
SELECT * from maindata where somedata4 LIKE "%data4.%"
This gives me back a list of all 6 entries below, however I want an additional column to show me if the current userdata.userId has a matching row and to include the amount column of that. If it doesn't have that row to default to a value of 0.
Table: maindata
id | somedata | somedata2 | somedata3 | somedata4
_________________________________________________
1 | data1.1 | data2.1 | data3.1 | data4.1
2 | data1.2 | data2.2 | data3.2 | data4.2
3 | data1.3 | data2.3 | data3.3 | data4.3
4 | data1.4 | data2.4 | data3.4 | data4.4
5 | data1.5 | data2.5 | data3.5 | data4.5
6 | data1.6 | data2.6 | data3.6 | data4.6
Table: userdata
id | itemId | amount | userId
_____________________________
1 | 6 | 4 | 1
2 | 4 | 4 | 26
3 | 4 | 2 | 1
It should search table maindata for WHERE somedata4 LIKE "%data4.%" and on each of those entries look in table userdata for userdata.amount with maindata.id = userdata.itemId WHERE maindata.userId = 1
Here's what I currently have for SQL
SELECT m.*, IFNULL(u.amount,0)
from maindata m
LEFT OUTER JOIN userdata u ON m.id = u.itemId
WHERE m.somedata4 LIKE "%data4.%"
What I'm missing is the filtering of only amounts from userdata.userId = 1, I want the entire list to show as it is in that query.
Expected Results:
id | somedata | somedata2 | somedata3 | somedata4 | amount
__________________________________________________________
1 | data1.1 | data2.1 | data3.1 | data4.1 | 0
2 | data1.2 | data2.2 | data3.2 | data4.2 | 0
3 | data1.3 | data2.3 | data3.3 | data4.3 | 0
4 | data1.4 | data2.4 | data3.4 | data4.4 | 4
5 | data1.5 | data2.5 | data3.5 | data4.5 | 0
6 | data1.6 | data2.6 | data3.6 | data4.6 | 2
SELECT m.*, IFNULL(u.amount,0) from maindata m LEFT OUTER JOIN
userdata u ON m.id = u.itemId WHERE m.userId = 1
Here is the SQL Query I was looking for (I think):
SELECT m.*, IFNULL(u.amount,0) AS "Amount"
FROM maindata m
LEFT OUTER JOIN userdata u ON m.id = u.itemId AND userid = 1
WHERE m.somedata4 LIKE "%data4.%"
It's giving me the desired results listed above, I just don't know if it's the most efficient way of handling this request.
SQLFiddle Here
I tried this solution but i am not sure if this is what you are looking for. Let me know.
-------EDIT--------
Now I'm sure about what you are looking for. I had just a couple of minutes so I didn't really optimized the code, but this seems to work except for the order of the fields, that can't be really modified since we're operating in two different sets of results.
By the way, this is the query
SELECT m.*, '0' as amount
from maindata m
left outer JOIN userdata u ON m.id = u.itemId
where (u.userid is null) and m.somedata4 LIKE '%data4.%'
UNION
SELECT m.*, u.amount
from maindata m
inner JOIN userdata u ON m.id = u.itemId
where u.userid = 1 and m.somedata4 LIKE '%data4.%'
and this is the updated fiddle
Hope this helps.