MySQL Join and Default - mysql

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.

Related

Combining three SQL queries into one

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

Querying MySQL tables for a item a user hasnt 'scored' yet

Tables
__________________ ________________________________
|______name________| |____________scores______________|
|___id___|__name___| |_id_|_user-id_|_name-id_|_score_|
| 1 | bob | | 1 | 3 | 1 | 5 |
| 2 | susan | | 2 | 1 | 3 | 4 |
| 3 | geoff | | 3 | 3 | 2 | 3 |
| 4 | larry | | 4 | 2 | 4 | 5 |
| 5 | peter | | 5 | 1 | 1 | 0 |
-------------------- ----------------------------------
Im looking to write a query that returns a RANDOM name from the 'name' table, that the user hasnt scored so far.
So given user '1' for example, it could return 'susan, larry or peter' as user '1' hasnt given them a score yet.
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id
WHERE votes.user_id = 1
AND (votes.score IS NULL);
So far I have this, but it doesnt seem to be working as I would like
(atm it doesnt return a random, but all, but this is wrong)
Any help would be appreciated.
If you are filtering on some field of outer joined table type of join is automatically changed to inner. In your case it's condition
votes.user_id = 1
So you need to move that condition from WHERE to ON
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id and votes.user_id = 1
WHERE (votes.score IS NULL);
Consider moving the condition from WHERE to JOIN ON clause since you are performing an OUTER JOIN else the effect would be same as INNER JOIN
LEFT JOIN votes
ON names.id = votes.name_id
AND votes.user_id = 1
WHERE votes.score IS NULL
ORDER BY RAND();
You could apply :
SELECT name FROM name join scores on name.id=scores.user_id WHERE scores.score=0
You can perform this as a sub-query
SELECT *
FROM names
WHERE id NOT IN (SELECT name_id FROM votes WHERE user_id=1)
ORDER BY RAND()
LIMIT 1

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.

INNER JOIN ? outcomes on two columns

I'm a little bit confused about which join to apply and where.
I have a mysql database that does betting in an IRC client.
It stores usernames , their guess and the eventual outcome of the game they bet on
the outcomes_table is like this
+--------------+
| id outcome |
+--------------+
| 1 win |
| 2 lose |
+--------------+
the user_table is like this
+----+----------+----------+-------------------+
| id | username | guess_id | bettingsession_id |
+----+----------+----------+-------------------+
| 1 | name1 | 1 | 1 |
| 2 | name2 | 1 | 2 |
| 3 | name3 | 2 | 2 |
4 name1 1 2
+----+----------+----------+-------------------+
the betting_session_table is like this:
+----+---------+
| id | result |
+----+---------+
| 1 | 1 |
| 2 | 2 |
+----+---------+
I want to get a list of the bets of a user with their guess joined to the outcome and the result joined to the
eg:
select each row a different bet username, guess (win/lose), result (win/lose)
Something like:
SELECT *
FROM user_table
INNER JOIN betting_session_table ON bettingsession_id = betting_session_table.id
INNER JOIN outcomes_table ON guess_id = outcomes_table.id
INNER JOIN outcomes_table ON result = outcomes_table.id
WHERE username = 'name1'
However this doesn't work, not sure but I don't think it lets me join the outcomes_table.id twice to two different columns but I want to this because the user may bet 'win' but result 'lose' etc.
EG: I want to return
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
| id | username | guess_id | id | outcome | betting_session_id | id | result | id | outcome |
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
| 1 | name1 | 1 | 1 | win | 1 | 1 | 1 | 1 | win |
| 4 | name1 | 1 | 1 | win | 2 | 2 | 2 | 2 | lose |
+----+----------+----------+----+---------+--------------------+----+--------+----+---------+
EDIT:
In the end I used two separate alias for each join which seems to work; here is the code from the actual table that works rather than the cut down example above.
SELECT *
FROM `xcoins_betting_log` A
LEFT JOIN `xcoins_betting_session` B ON A.betting_session_id = B.id
LEFT JOIN `xcoins_common_tables`.`xcoins_betting_outcomes` C ON A.guess_id = C.id
LEFT JOIN `xcoins_common_tables`.`xcoins_betting_outcomes` D ON B.outcome_id = D.id
WHERE `user_id` =9
I'm not sure if this is what you want, but I hope so.
SELECT
usr.*,
res.outcome,
IF(res.id = usr.guess_id, 'User win', 'User lose') AS result
FROM user_table AS usr
INNER JOIN betting_session_table AS bet ON
bet.id = usr.bettingsession_id
INNER JOIN outcomes_table AS res ON
res.id = bet.result
WHERE usr.username = 'name1'
Choose correct join
The most common joins is LEFT and INNER. Lets say the users have placed their bets, but the football game (or whatever) isn't completed yet, then you won't have the row in the outcomes_table right? The game isn't finished so the results will come later.
If you use INNER JOIN, the row in the outcomes_table won't match for unfinished games --- INNER JOIN requires matches.
If you want to see the bets also before the game has started, you can use LEFT JOIN. LEFT JOIN won't remove rows that hasn't got any outcome, the users will still be listed --- LEFT JOIN doesn't care.
INNER JOIN: Game must have result
LEFT JOIN: Game might have result

How do I select all the dealers that did not have an order?

I am trying to join two tables and only select the dealers that did not have their promo code used on any order.
How can I do this?
I'm trying this below, but it's not working right. In the example I want to get just Bob, since his promo_code hasn't been used in any orders.
SELECT d.`name`
FROM z_dealer d
LEFT OUTER JOIN z_order o ON (d.promo_code = o.promo_code)
AND o.promo_code IS NULL
Here are my tables...
mysql> select * from z_dealer;
+----+------+------------+
| id | name | promo_code |
+----+------+------------+
| 1 | John | holiday |
| 2 | Suzy | special |
| 3 | Bob | laborday |
+----+------+------------+
mysql> Select * from z_order;
+----+-------+------------+
| id | total | promo_code |
+----+-------+------------+
| 1 | 10 | holiday |
| 2 | 20 | special |
| 3 | 15 | holiday |
| 4 | 45 | special |
+----+-------+------------+
SELECT d.`name` FROM z_dealer d LEFT JOIN z_order o ON (d.promo_code = o.promo_code) WHERE o.promo_code IS NULL
Have you tried INNER JOIN? or You can try IN like this :
SELECT d.name
FROM z_dealer d
WHERE d.promo_code not in( SELECT promo_code FROM z_order)
I'm not entirely sure why it's not working in your example code. I've created the same tables locally and when I run the script you provided I get the single 'Bob' answer.
SELECT d.name
FROM z_dealer d
LEFT OUTER JOIN z_order o ON (d.promo_code = o.promo_code)
AND o.promo_code IS NULL
What results are you seeing exactly?