I'm trying to output a default row when there's no row found in the query.
This is a sample of my query:
SELECT
COALESCE(site, 'STE') as site,
instrument,
field
FROM Table1
WHERE site IN ('East', 'West')
AND DATE(tstamp) = "2016-09-07"
ORDER BY id desc
The output is
+------+------------+-------+
| site | instrument | field |
+------+------------+-------+
| West | 0 | 0 |
+------+------------+-------+
For the tsamp 2016-09-07 we have a row for the site "West" and there's no row for "East". I tried to search and found that I can use COALESCE and also tried IFNULL but I'm only getting the output above. I also tried if(count(site) = 0, "STE", site) but i can't get it to work.
My expected result is
+------+------------+-------+
| site | instrument | field |
+------+------------+-------+
| West | 0 | 0 |
| STE | NULL | NULL |
+------+------------+-------+
I hope you guys can help me. Thanks in advance
Both coalesce() and ifnull() work on a row basis, meaning they can replace a null value if that null value exists in a record. However, they cannot create a record that does not exist - and you do not have any records matching East (or STE).
A possible solution is to create a table that has all possible values for the site field and you can left join on this table:
SELECT
COALESCE(Table1.site, "STE") as site,
Table1.instrument,
Table1.field
FROM LookupTable lt
LEFT JOIN Table1 ON lt.site=Table1.site
WHERE lt.site IN ('East', 'West')
AND DATE(Table1.tstamp) = "2016-09-07"
ORDER BY id desc
If 'STE' isn't in table1.site then it won't come back in the results.
You could do a union instead:
SELECT
site,
instrument,
field
FROM (SELECT 'West' [Site], 0 [instrument], 0 [field]) table1
WHERE site IN ('East', 'West')
UNION
SELECT 'STE', NULL, NULL
Note that by using "STE" you're looking for the column name "STE", not the value 'STE' (single quotes)
EDIT
You need a control table to dictate which values to look for, or you have to hard code your rules. You can't look for something that's missing without first specifying the things that should be there.
Here's an option:
--Create and populate reference table "sites"
create table sites
([site] char (4))
INSERT INTO sites VALUES ('East')
INSERT INTO sites VALUES ('West')
-- Query against reference table
SELECT
ISNULL(table1.site, 'STE'),
instrument,
field
FROM (SELECT 'West' [Site], 0 [instrument], 0 [field]) table1
RIGHT JOIN sites on table1.[Site] = sites.[Site]
--or
-- Query against reference table
SELECT
ISNULL(table1.site, 'STE'),
instrument,
field
FROM sites
LEFT JOIN (SELECT 'West' [Site], 0 [instrument], 0 [field]) table1 on table1.[Site] = sites.[Site]
Let me know if you have questions on how this works.
Related
In a MySQL JOIN, what is the difference between ON and USING()? As far as I can tell, USING() is just more convenient syntax, whereas ON allows a little more flexibility when the column names are not identical. However, that difference is so minor, you'd think they'd just do away with USING().
Is there more to this than meets the eye? If yes, which should I use in a given situation?
It is mostly syntactic sugar, but a couple differences are noteworthy:
ON is the more general of the two. One can join tables ON a column, a set of columns and even a condition. For example:
SELECT * FROM world.City JOIN world.Country ON (City.CountryCode = Country.Code) WHERE ...
USING is useful when both tables share a column of the exact same name on which they join. In this case, one may say:
SELECT ... FROM film JOIN film_actor USING (film_id) WHERE ...
An additional nice treat is that one does not need to fully qualify the joining columns:
SELECT film.title, film_id -- film_id is not prefixed
FROM film
JOIN film_actor USING (film_id)
WHERE ...
To illustrate, to do the above with ON, we would have to write:
SELECT film.title, film.film_id -- film.film_id is required here
FROM film
JOIN film_actor ON (film.film_id = film_actor.film_id)
WHERE ...
Notice the film.film_id qualification in the SELECT clause. It would be invalid to just say film_id since that would make for an ambiguity:
ERROR 1052 (23000): Column 'film_id' in field list is ambiguous
As for select *, the joining column appears in the result set twice with ON while it appears only once with USING:
mysql> create table t(i int);insert t select 1;create table t2 select*from t;
Query OK, 0 rows affected (0.11 sec)
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 1 row affected (0.19 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select*from t join t2 on t.i=t2.i;
+------+------+
| i | i |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
mysql> select*from t join t2 using(i);
+------+
| i |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql>
Thought I would chip in here with when I have found ON to be more useful than USING. It is when OUTER joins are introduced into queries.
ON benefits from allowing the results set of the table that a query is OUTER joining onto to be restricted while maintaining the OUTER join. Attempting to restrict the results set through specifying a WHERE clause will, effectively, change the OUTER join into an INNER join.
Granted this may be a relative corner case. Worth putting out there though.....
For example:
CREATE TABLE country (
countryId int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
country varchar(50) not null,
UNIQUE KEY countryUIdx1 (country)
) ENGINE=InnoDB;
insert into country(country) values ("France");
insert into country(country) values ("China");
insert into country(country) values ("USA");
insert into country(country) values ("Italy");
insert into country(country) values ("UK");
insert into country(country) values ("Monaco");
CREATE TABLE city (
cityId int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
countryId int(10) unsigned not null,
city varchar(50) not null,
hasAirport boolean not null default true,
UNIQUE KEY cityUIdx1 (countryId,city),
CONSTRAINT city_country_fk1 FOREIGN KEY (countryId) REFERENCES country (countryId)
) ENGINE=InnoDB;
insert into city (countryId,city,hasAirport) values (1,"Paris",true);
insert into city (countryId,city,hasAirport) values (2,"Bejing",true);
insert into city (countryId,city,hasAirport) values (3,"New York",true);
insert into city (countryId,city,hasAirport) values (4,"Napoli",true);
insert into city (countryId,city,hasAirport) values (5,"Manchester",true);
insert into city (countryId,city,hasAirport) values (5,"Birmingham",false);
insert into city (countryId,city,hasAirport) values (3,"Cincinatti",false);
insert into city (countryId,city,hasAirport) values (6,"Monaco",false);
-- Gah. Left outer join is now effectively an inner join
-- because of the where predicate
select *
from country left join city using (countryId)
where hasAirport
;
-- Hooray! I can see Monaco again thanks to
-- moving my predicate into the ON
select *
from country co left join city ci on (co.countryId=ci.countryId and ci.hasAirport)
;
Wikipedia has the following information about USING:
The USING construct is more than mere syntactic sugar, however, since
the result set differs from the result set of the version with the
explicit predicate. Specifically, any columns mentioned in the USING
list will appear only once, with an unqualified name, rather than once
for each table in the join. In the case above, there will be a single
DepartmentID column and no employee.DepartmentID or
department.DepartmentID.
Tables that it was talking about:
The Postgres documentation also defines them pretty well:
The ON clause is the most general kind of join condition: it takes a
Boolean value expression of the same kind as is used in a WHERE
clause. A pair of rows from T1 and T2 match if the ON expression
evaluates to true.
The USING clause is a shorthand that allows you to take advantage of
the specific situation where both sides of the join use the same name
for the joining column(s). It takes a comma-separated list of the
shared column names and forms a join condition that includes an
equality comparison for each one. For example, joining T1 and T2 with
USING (a, b) produces the join condition ON T1.a = T2.a AND T1.b =
T2.b.
Furthermore, the output of JOIN USING suppresses redundant columns:
there is no need to print both of the matched columns, since they must
have equal values. While JOIN ON produces all columns from T1 followed
by all columns from T2, JOIN USING produces one output column for each
of the listed column pairs (in the listed order), followed by any
remaining columns from T1, followed by any remaining columns from T2.
Database tables
To demonstrate how the USING and ON clauses work, let's assume we have the following post and post_comment database tables, which form a one-to-many table relationship via the post_id Foreign Key column in the post_comment table referencing the post_id Primary Key column in the post table:
The parent post table has 3 rows:
| post_id | title |
|---------|-----------|
| 1 | Java |
| 2 | Hibernate |
| 3 | JPA |
and the post_comment child table has the 3 records:
| post_comment_id | review | post_id |
|-----------------|-----------|---------|
| 1 | Good | 1 |
| 2 | Excellent | 1 |
| 3 | Awesome | 2 |
The JOIN ON clause using a custom projection
Traditionally, when writing an INNER JOIN or LEFT JOIN query, we happen to use the ON clause to define the join condition.
For example, to get the comments along with their associated post title and identifier, we can use the following SQL projection query:
SELECT
post.post_id,
title,
review
FROM post
INNER JOIN post_comment ON post.post_id = post_comment.post_id
ORDER BY post.post_id, post_comment_id
And, we get back the following result set:
| post_id | title | review |
|---------|-----------|-----------|
| 1 | Java | Good |
| 1 | Java | Excellent |
| 2 | Hibernate | Awesome |
The JOIN USING clause using a custom projection
When the Foreign Key column and the column it references have the same name, we can use the USING clause, like in the following example:
SELECT
post_id,
title,
review
FROM post
INNER JOIN post_comment USING(post_id)
ORDER BY post_id, post_comment_id
And, the result set for this particular query is identical to the previous SQL query that used the ON clause:
| post_id | title | review |
|---------|-----------|-----------|
| 1 | Java | Good |
| 1 | Java | Excellent |
| 2 | Hibernate | Awesome |
The USING clause works for Oracle, PostgreSQL, MySQL, and MariaDB. SQL Server doesn't support the USING clause, so you need to use the ON clause instead.
The USING clause can be used with INNER, LEFT, RIGHT, and FULL JOIN statements.
SQL JOIN ON clause with SELECT *
Now, if we change the previous ON clause query to select all columns using SELECT *:
SELECT *
FROM post
INNER JOIN post_comment ON post.post_id = post_comment.post_id
ORDER BY post.post_id, post_comment_id
We are going to get the following result set:
| post_id | title | post_comment_id | review | post_id |
|---------|-----------|-----------------|-----------|---------|
| 1 | Java | 1 | Good | 1 |
| 1 | Java | 2 | Excellent | 1 |
| 2 | Hibernate | 3 | Awesome | 2 |
As you can see, the post_id is duplicated because both the post and post_comment tables contain a post_id column.
SQL JOIN USING clause with SELECT *
On the other hand, if we run a SELECT * query that features the USING clause for the JOIN condition:
SELECT *
FROM post
INNER JOIN post_comment USING(post_id)
ORDER BY post_id, post_comment_id
We will get the following result set:
| post_id | title | post_comment_id | review |
|---------|-----------|-----------------|-----------|
| 1 | Java | 1 | Good |
| 1 | Java | 2 | Excellent |
| 2 | Hibernate | 3 | Awesome |
You can see that this time, the post_id column is deduplicated, so there is a single post_id column being included in the result set.
Conclusion
If the database schema is designed so that Foreign Key column names match the columns they reference, and the JOIN conditions only check if the Foreign Key column value is equal to the value of its mirroring column in the other table, then you can employ the USING clause.
Otherwise, if the Foreign Key column name differs from the referencing column or you want to include a more complex join condition, then you should use the ON clause instead.
For those experimenting with this in phpMyAdmin, just a word:
phpMyAdmin appears to have a few problems with USING. For the record this is phpMyAdmin run on Linux Mint, version: "4.5.4.1deb2ubuntu2", Database server: "10.2.14-MariaDB-10.2.14+maria~xenial - mariadb.org binary distribution".
I have run SELECT commands using JOIN and USING in both phpMyAdmin and in Terminal (command line), and the ones in phpMyAdmin produce some baffling responses:
1) a LIMIT clause at the end appears to be ignored.
2) the supposed number of rows as reported at the top of the page with the results is sometimes wrong: for example 4 are returned, but at the top it says "Showing rows 0 - 24 (2503 total, Query took 0.0018 seconds.)"
Logging on to mysql normally and running the same queries does not produce these errors. Nor do these errors occur when running the same query in phpMyAdmin using JOIN ... ON .... Presumably a phpMyAdmin bug.
Short answer:
USING: when clause is ambiguous
ON: when clause has different comparison params
Table structure and data (I know data in IP/domain fields might not make much sense, but this is for illustration purposes):
rec_id | account_id | product_id | ip | domain | some_data
----------------------------------------------------------------------------
1 | 1 | 1 | 192.168.1.1 | 127.0.0.1/test | abc
2 | 1 | 1 | 192.168.1.1 | 127.0.0.1/other | xyz
3 | 1 | 1 | 192.168.1.2 | 127.0.0.1/test | ooo
Table has unique index ip_domain combined from ip and domain fields (so records with identical values in both fields can't exist).
In each case I know values for account_id, product_id, ip, domain fields, and I need to get other rows that have the SAME account_id, product_id values and one (or both) of ip, domain values are DIFFERENT.
Example: I know that account_id=1, product_id=1, ip=192.168.1.1, domain=127.0.0.1/test (so it matches rec_id 1), I need to select records with IDs 2 and 3 (because record 2 has different domain and record 3 has different ip).
So, I used query:
SELECT * FROM table WHERE
account_id='1' AND product_id='1' AND ip!='192.168.1.1' AND domain!='127.0.0.1/test'
Of course, it returned 0 rows. Looked at mysql multiple where and where not in and wrote:
SELECT * FROM table WHERE
account_id='1' AND product_id='1' AND installation_ip NOT IN ('192.168.1.1') AND installation_domain NOT IN ('127.0.0.1/test')
My guess is that this query is identical (just formatted different way), so 0 rows again. Found some more examples too, but none worked in my case.
The syntax is correct, but you're using the wrong logical operation
SELECT *
FROM table
WHERE account_id='1' AND product_id='1' AND
(ip != '192.168.1.1' OR domain != '127.0.0.1/test')
Select * from table
Where ROWID <> myRowid
And account_id = '1'
And product_id = '1';
myRowid is the unique id given by your dbms to each record, in this case you need to retrieve it with your select statement and then pass it back when using this select. This will return all the rows with account_id = 1 and product_id = 1 except the one you have selected.
If your inputs are not defined/or if you want list then you may be look at Group By clause. Also, you may look at group_concat
Query would be something like:
SELECT ACCOUNT_ID, PRODUCT_ID, GROUP_CONCAT(DISTINCT IP||'|'||DOMAIN, ','), COUNT(1)
FROM TABLE
GROUP BY ACCOUNT_ID, PRODUCT_ID
P.S.: I dont have mysql installed hence the query syntax is not verified
I believe this should be relatively simple but I can't seem to figure out the logic to it.
I'll start with what I'm looking for. I wanted to be able to pass in values (such as e-mail addresses) through a SELECT query and have it output whether those rows exist or not. It would be something like this:
# PASS Values test#test.com and example#example.com in.
# For these purposes let's pretend that test#test.com exists
# and example#example.com does not.
+----------------------+--------+
| E-mail | Exists |
+----------------------+--------+
| test#test.com | 1 |
| example#example.com | 0 |
+----------------------+--------+
There is the answer here for a single result: MySQL EXISTS return 1 or 0 but I would like to expand on that and am having issues figuring it out.
Any help would be great. Thanks!
Assuming I'm understanding your question correctly, you can't do this with in alone. One option is to create a subquery with an outer join, and then use case:
select t.email,
case when yt.email is not null then 1 else 0 end `exists`
from (select 'test#test.com' as email union all select 'example#example.com') t
left join yourtable yt on t.email = yt.email
Note: backticks are needed on exists in the statement.
I have 2 MySQL tables.
One table has a column that lists all the states
colStates | column2 | column 3
------------------------------
AK | stuff | stuff
AL | stuff | stuff
AR | stuff | stuff
etc.. | etc.. | etc..
The second table has a column(randomStates) with all NULL values that need to be populated with a randomly selected state abbreviation.
Something like...
UPDATE mytable SET `randomStates`= randomly selected state value WHERE randomStates IS NULL
Can someone help me with this statement. I have looked around at other posts, but I don't understand them.
this works for me with trial data in SQLite:
UPDATE mytable
SET randomStates = (SELECT colStates FROM
(SELECT * FROM first_table ORDER BY RANDOM())
WHERE randomStates IS NULL)
without the first SELECT portion, you end up with the same random value inserted into all the NULL randomStates field. (i.e. if you just do SELECT StateValue FROM counts ORDER BY RANDOM() you don't get what you want).
I have two tables, "records", and "info".
The "records" table looks like:
mysql> SELECT * FROM records WHERE num = '7';
+-----+--------+----+------+-----+-----+------------+-----------+----------+---------------------+
| id | city | st | type | num | val | startdate | status | comments | updated |
+-----+--------+----+------+-----+-----+------------+-----------+----------+---------------------+
| 124 | Encino | CA | AAA | 7 | 1 | 1993-09-01 | allocated | | 2014-02-26 08:16:07 |
+-----+--------+----+------+-----+-----+------------+-----------+----------+---------------------+
and so on. Think of the "num" field in this table as a Company ID.
The "info" table contains information about certain companies, and uses that company id as a unique identifier. Not all companies listed in "records" will be in "info". An example of the "info" table:
mysql> SELECT * FROM info LIMIT 2;
+-----+-------+--------------------------+---------------------+
| org | name | description | updated |
+-----+-------+--------------------------+---------------------+
| 0 | ACME | | 2014-02-19 10:35:39 |
| 1 | AT&T | Some Phone Company, Inc. | 2014-02-18 15:29:50 |
+-----+-------+--------------------------+---------------------+
So "org" here will match "num" in the first table.
I want to be able to run a query that returns, on one line, everything but 'id', 'type' and 'val' from the 1st table, and IF APPLICABLE, the 'name' and 'description' from the 2nd table.
I can achieve what I want using this query:
SELECT city,st,num,startdate,status,comments,updated, \
( SELECT name FROM info WHERE org = '7') AS name, \
( SELECT description FROM info WHERE org = '7') AS description \
FROM records WHERE num = '7'
But I see at least two problems with it:
It seems inefficient to run two subqueries
When there is no record in "info", NULL is printed for the name and
description. I would like to print some string instead.
To address the first problem, I tried to return an array. But when no corresponding record exists in the "info" table, then I get nothing, not even the valid info from the "records" table. Here's my array query:
SELECT city,st,num,startdate,status,comments,updated,asinfo.name AS name,asinfo.description AS description \
FROM records, \
( SELECT name,description FROM info WHERE org = '7') AS asinfo \
WHERE num = '7'
This query works fine if a given company id exists in both tables.
To address the second problem, I tried various incantations of IFNULL and coalesce, to no avail.
I'd appreciate any insight.
Thanks.
Apply LEFT JOIN syntax:
SELECT
r.city,
r.st,
r.num,
r.startdate,
r.status,
r.comments,
r.updated,
IF(d.name IS NULL, 'Default', d.name) AS name,
IF(d.description IS NULL, 'Default', d.description) AS description
FROM
records AS r
LEFT JOIN info AS d ON r.num=d.org
WHERE
r.num='7'
that will work such way: LEFT JOIN looks into first table, and, if there are no corresponding records in second, it applies NULL. So you'll discover that with IF (or IFNULL) and do substitution of default string.
Use a LEFT JOIN to get null values when there's no matching row in the info table.
SELECT city,st,num,startdate,status,comments,updated,
IFNULL(name, 'Default Name') name,
IFNULL(description, 'Default Description') description
FROM records r
LEFT JOIN info i ON r.num = i.org
WHERE r.num = 7
It sounds like a simple LEFT JOIN from record to info will do the trick.
LEFT JOIN rather than JOIN in order to ensure you ALWAYS get all rows from the record table, and then the corresponding data in info table if a xref exists for that ID.
Whether using your sub-queries or using joins, if you always want to see all rows in record table, then you will always get NULLs corresponding to the info table where no xref exists. The only way to avoid that is to run some code that calls everything from record, and then iterates over the results to query info, to conditionally add to the record data.