I'm using Mysql and I'm stuck on what I hope is a simple problem.
I need to select data from a table if one condition is true or another condition is true.
One select I tried returns data, but a lot more data than it should. Indeed the table contains just 66 records however my query is returning 177 records. I'm misunderstanding something.
I need to select data if ['city' is equal to a value and 'type' is golden] or 'type' is within a category called 'charms'
I've tried this query
SELECT b.*
FROM bubbles b, bubble_types bt
WHERE
b.city = 10
AND b.type = 'golden'
OR bt.category = 'charm'
AND bt.type = b.type;
and this one (which doesn't work at all but may be closer to the mark?)
SELECT b.*
IF(b.city = 10, b.type = 'golden'),
IF(bt.category = 'charm', bt.type = b.type)
FROM bubbles b, bubble_types bt;
Hopefully what I want makes sense?
I should get about 10 rows from the 66 of those bubbles in city 10 that are 'golden', or those bubbles whose type field puts them in category 'charm'.
Thanks;
edit sample table data for bubble_types:
+----+----------+------------+
| id | category | type |
+----+----------+------------+
| 1 | bubble | golden |
| 2 | charm | teleport |
| 3 | charm | blow |
| 4 | badge | reuser |
| 5 | badge | winner |
| 6 | badge | loothunter |
| 7 | charm | freeze |
| 8 | badge | reuser |
| 9 | badge | winner |
| 10 | badge | loothunter |
+----+----------+------------+
mysql> describe bubbles;
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| city | bigint(20) | YES | | NULL | |
| type | varchar(32) | YES | | NULL | |
| taken_by | bigint(20) | YES | | NULL | |
| taken_time | bigint(20) | YES | | NULL | |
| label | varchar(256) | YES | | NULL | |
| description | varchar(16384) | YES | | NULL | |
| created | datetime | YES | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
You are so close! Take the "WHERE"-ness of your first go with the parenthesis of your second (and add an appropriate ON clause to your JOIN):
SELECT b.*
FROM bubbles b
JOIN bubble_types bt
ON b.type = bt.type
WHERE
(b.city = 10 AND b.type = 'golden')
OR
(bt.category = 'charm' AND bt.type = b.type);
The devil is in the details of associativity of AND and OR in the where clauses. When in doubt, use parenthesis to make your intentions explicit.
Related
I'm having troubles in making a rather difficult MySQL query work. I've been trying, but creating complex queries has never been my strong side.
This query includes 4 tables, which I'll describe of course.
First, we have song table, which I need to select the needed info from.
+--------------+-----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------+------+-----+---------+----------------+
| ID | int(6) | NO | PRI | - | auto_increment |
| Anime | char(100) | NO | | - | |
| Title | char(100) | NO | | - | |
| Type | char(20) | NO | | - | |
| Singer | char(50) | NO | | - | |
| Youtube | char(30) | NO | | - | |
| Score | double | NO | | 0 | |
| Ratings | int(8) | NO | | 0 | |
| Favourites | int(7) | NO | | 0 | |
| comments | int(11) | NO | | 0 | |
| release_year | int(4) | NO | | 2019 | |
| season | char(10) | NO | | Spring | |
+--------------+-----------+------+-----+---------+----------------+
Then we have song_ratings, which basically represents the lists of each user, since once you rate a song, it appears on your list.
+------------+----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+-------------------+----------------+
| ID | int(11) | NO | PRI | 0 | auto_increment |
| UserID | int(11) | NO | MUL | 0 | |
| SongID | int(11) | NO | MUL | 0 | |
| Rating | double | NO | | 0 | |
| RatedAt | datetime | NO | | CURRENT_TIMESTAMP | |
| Favourited | int(1) | NO | | 0 | |
+------------+----------+------+-----+-------------------+----------------+
Users have the option to create custom lists(playlists), and this is the table which they are stored in. This is table lists.
+------------+-----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------+------+-----+-------------------+----------------+
| ID | int(11) | NO | PRI | 0 | auto_increment |
| userID | int(11) | NO | MUL | 0 | |
| name | char(50) | NO | | - | |
| likes | int(11) | NO | | 0 | |
| favourites | int(11) | NO | | 0 | |
| created_at | datetime | NO | | CURRENT_TIMESTAMP | |
| cover | char(100) | NO | | - | |
| locked | int(1) | NO | | 0 | |
| private | int(1) | NO | | 0 | |
+------------+-----------+------+-----+-------------------+----------------+
And finally, the table which contains all the songs that have been added to any playlists, called list_elements.
+--------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | 0 | auto_increment |
| listID | int(11) | NO | MUL | 0 | |
| songID | int(11) | NO | MUL | 0 | |
+--------+---------+------+-----+---------+----------------+
What my query needs to do is list all the songs that are on the list of a user, basically these are the record in song_ratings where the userID = ?(obviously the ID of the user), but are not on a specific playlist(has no record in list_elements) where the ID/listID = ?(the ID of that playlist).
This is the query I've been using so far, but after a while I had realized this doesn't actually work the way I wanted to.
SELECT DISTINCT
COUNT(*)
FROM
song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
LEFT JOIN list_elements ON song_ratings.songID = list_elements.songID
WHERE
song_ratings.userID = 34 AND list_elements.songID IS NULL
I have also tried something like this, and several variants of it
SELECT DISTINCT
COUNT(*)
FROM
song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
INNER JOIN lists ON lists.userID = song_ratings.userID
LEFT JOIN list_elements ON song_ratings.songID = list_elements.songID
WHERE
song_ratings.userID = 34 AND lists.ID = 1
To make it easier, here's a SQL Fiddle, with all the necessary tables and records in them.
What you need to know. When you check for the playlist with the ID of 1, the query needs to return 23(basically all matches).
When you do the same with the ID 4, it need to return 21, if the query works correctly, because the playlist 1 is empty, thus all of the songs in the table song_ratings can be added to it(at least the ones that exist in song table, which is only half of the overall records now).
But playlist 4 already has 2 songs added to it, so only 21 are left available for adding.
Or in case the number are wrong, playlist 1 needs to return all matches. playlist 4 need to return all matches-2(because 2 songs are already added).
The userID needs to remain the same(34), and there are no records with different ID, so don't change it.
You could try subquery with NOT IN clause
SELECT DISTINCT
COUNT(*)
FROM
song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
WHERE
song_ratings.userID = 34 AND song.ID not in (select songID from list_elements group by songID)
Your original query was almost correct. When you use a column from a joined table with a LEFT JOIN in the WHERE-clause, it causes the LEFT JOIN to turn into an INNER JOIN.
You can put the condition into the ON-clause:
SELECT COUNT(*)
FROM song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
LEFT JOIN list_elements ON song_ratings.songID = list_elements.songID
AND list_elements.songID IS NULL
WHERE song_ratings.userID = 34
Using JOINs in MySQL is faster than using subqueries, this would probably be faster as well.
Btw, you do not need DISTINCT when you only have COUNT(*). The COUNT(*) returns only one row so there is no need to take distinct values from one value.
I need to select and display information from a pair of MySQL tables but the syntax eludes me. Specifically, I need to JOIN the data from the cwd_user table with the data from the cwd_user_attribute table on the field cwd_user.id == cwd_user_attribute.user_id, but I also need to display values from several entries in the cwd_user_attribute table in a single line. It's the latter that eludes me. Here are the gory details:
Given two tables:
mysql (crowd#prod:crowddb)> desc cwd_user;
+---------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+-------+
| id | bigint(20) | NO | PRI | NULL | |
| user_name | varchar(255) | NO | | NULL | |
| active | char(1) | NO | MUL | NULL | |
| created_date | datetime | NO | | NULL | |
| updated_date | datetime | NO | | NULL | |
| display_name | varchar(255) | YES | | NULL | |
| directory_id | bigint(20) | NO | MUL | NULL | |
+---------------------+--------------+------+-----+---------+-------+
mysql (crowd#prod:crowddb)> desc cwd_user_attribute;
+-----------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+---------+-------+
| id | bigint(20) | NO | PRI | NULL | |
| user_id | bigint(20) | NO | MUL | NULL | |
| directory_id | bigint(20) | NO | MUL | NULL | |
| attribute_name | varchar(255) | NO | | NULL | |
| attribute_value | varchar(255) | YES | | NULL | |
+-----------------------+--------------+------+-----+---------+-------+
Assume that there are up to seven possible values for cwd_user_attribute.attribute_name and I'm interested in four of them: lastAuthenticated, Team, Manager Notes. Example:
mysql (crowd#prod:crowddb)> select * from cwd_user_attribute where user_id = (select id from cwd_user where user_name = 'gspinrad');
+---------+---------+--------------+-------------------------+----------------------------------+
| id | user_id | directory_id | attribute_name | attribute_value |
+---------+---------+--------------+-------------------------+----------------------------------+
| 65788 | 32844 | 1 | invalidPasswordAttempts | 0 |
| 65787 | 32844 | 1 | lastAuthenticated | 1473360428804 |
| 65790 | 32844 | 1 | passwordLastChanged | 1374005378040 |
| 65789 | 32844 | 1 | requiresPasswordChange | false |
| 4292909 | 32844 | 1 | Team | Engineering - DevOps |
| 4292910 | 32844 | 1 | Manager | Matt Karaffa |
| 4292911 | 32844 | 1 | Notes | Desk 32:2:11 |
+---------+---------+--------------+-------------------------+----------------------------------+
5 rows in set (0.00 sec)
I can get a list of the users sorted by lastAuthenticated with this query:
SELECT cwd_user.user_name, cwd_user.id, cwd_user.display_name, from_unixtime(cwd_user_attribute.attribute_value/1000) as last_login FROM cwd_user JOIN cwd_directory ON cwd_user.directory_id = cwd_directory.id JOIN cwd_user_attribute ON cwd_user.id = cwd_user_attribute.user_id AND cwd_user_attribute.attribute_name='lastAuthenticated' WHERE DATEDIFF((NOW()), (from_unixtime(cwd_user_attribute.attribute_value/1000))) > 90 and cwd_user.active='T' order by last_login limit 4;
Result:
+-----------------------+---------+-----------------------+---------------------+
| user_name | id | display_name | last_login |
+-----------------------+---------+-----------------------+---------------------+
| jenkins-administrator | 1605636 | Jenkins Administrator | 2011-10-27 17:28:05 |
| sonar-administrator | 1605635 | Sonar Administrator | 2012-02-06 15:59:59 |
| jfelix | 1605690 | Joey Felix | 2012-02-06 19:15:15 |
| kbitters | 3178497 | Kitty Bitters | 2013-09-03 10:09:59 |
What I need to add to the output is the value of cwd_user_attribute.attribute_value where cwd_user_attribute.attribute_name is Team, Manager, and/or Notes. The output would look something like this:
+-----------------------+---------+-----------------------+-------------------------------------------------------------------+
| user_name | id | display_name | last_login | Team | Manager | Notes |
+-----------------------+---------+-----------------------+-------------------------------------------------------------------+
| jenkins-administrator | 1605636 | Jenkins Administrator | 2011-10-27 17:28:05 | Internal | Internal | |
| sonar-administrator | 1605635 | Sonar Administrator | 2012-02-06 15:59:59 | Internal | Internal | |
| jfelix | 1605690 | Joey Felix | 2012-02-06 19:15:15 | Hardware Eng. | Gary Spinrad | Desk 32:1:51 |
| kbitters | 3178497 | Kitty Bitters | 2013-09-03 10:09:59 | Software QA | Matt Karaffa | Desk 32:2:01 |
+-----------------------+---------+-----------------------+-------------------------------------------------------------------+
You can achieve that result with an additional LEFT JOIN with the attribute table. Then use GROUP BY and aggregated CASE statements to pivot the result (rows to columns).
SELECT
cwd_user.user_name,
cwd_user.id,
cwd_user.display_name,
from_unixtime(cwd_user_attribute.attribute_value/1000) as last_login,
MIN(CASE WHEN attr2.attribute_name = 'TEAM' THEN attr2.attribute_value END) as Team,
MIN(CASE WHEN attr2.attribute_name = 'Manager' THEN attr2.attribute_value END) as Manager,
MIN(CASE WHEN attr2.attribute_name = 'Notes' THEN attr2.attribute_value END) as Notes
FROM
cwd_user
JOIN
cwd_user_attribute ON cwd_user.id = cwd_user_attribute.user_id
AND cwd_user_attribute.attribute_name='lastAuthenticated'
LEFT JOIN
cwd_user_attribute attr2 ON cwd_user.id = attr2.user_id
AND attr2.attribute_name IN ('Team', 'Manager', 'Notes')
WHERE
DATEDIFF((NOW()), (from_unixtime(cwd_user_attribute.attribute_value/1000))) > 90
AND cwd_user.active = 'T'
GROUP BY
cwd_user.id
ORDER BY
last_login
LIMIT 4
With strict mode you would need to list all not aggregated columns in the GROUP BY clause
GROUP BY
cwd_user.user_name,
cwd_user.id,
cwd_user.display_name,
cwd_user_attribute.attribute_value
Another way is just to use three LEFT JOINs (one join per attribute name):
SELECT
cwd_user.user_name,
cwd_user.id,
cwd_user.display_name,
from_unixtime(cwd_user_attribute.attribute_value/1000) as last_login,
attr_team.attribute_value as Team,
attr_manager.attribute_value as Manager,
attr_notes.attribute_value as Notes
FROM cwd_user
JOIN cwd_user_attribute
ON cwd_user.id = cwd_user_attribute.user_id
AND cwd_user_attribute.attribute_name='lastAuthenticated'
LEFT JOIN cwd_user_attribute attr_team
ON cwd_user.id = attr2.user_id
AND attr2.attribute_name = 'Team'
LEFT JOIN cwd_user_attribute attr_manager
ON cwd_user.id = attr2.user_id
AND attr2.attribute_name = 'Manager'
LEFT JOIN cwd_user_attribute attr_notes
ON cwd_user.id = attr2.user_id
AND attr2.attribute_name = 'Notes'
WHERE DATEDIFF((NOW()), (from_unixtime(cwd_user_attribute.attribute_value/1000))) > 90
and cwd_user.active='T'
order by last_login limit 4
Note: I have removed the join with directory table because you seem not to use it. Add it again, if you need it for filtering.
Note 2: Some attributes that you often use for a search (like lastAuthenticated) should be converted to indexed columns in the users table to improve the search performance.
I have a simple table:
+-------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+-------+
| ID | bigint(20) | NO | PRI | NULL | |
| AdmissionDateTime | datetime | NO | | NULL | |
| AdmissionEvent | varchar(255) | YES | | NULL | |
| DischargeDateTime | datetime | YES | | NULL | |
| DischargeEvent | varchar(255) | YES | | NULL | |
| DemographicId | bigint(20) | NO | MUL | NULL | |
| FacilityId | bigint(20) | YES | MUL | NULL | |
| VisitId | bigint(20) | NO | MUL | NULL | |
| WardId | bigint(20) | NO | MUL | NULL | |
+-------------------+--------------+------+-----+---------+-------+
On which I run the following JPA (Spring-data) query:
#Query("SELECT w FROM WardTransaction w WHERE w.id = (SELECT MAX(x.id) FROM
WardTransaction x WHERE w = x AND w.visit = :visit)")
public WardTransaction findCurrent(#Param("visit") Visit visit);
On occasions I get the following exception.
org.springframework.dao.IncorrectResultSizeDataAccessException: More than one
result was returned from Query.getSingleResult(); nested exception is
javax.persistence.NonUniqueResultException: More than one result was returned from
Query.getSingleResult()
I have not been able to work out why this is happening. It does not seem to make a lot of sense to me as there can only be one 'MAX' - especially on Id (I have used 'admissionDate' in the past).
Any assistance appreciated.
why are you selecting table ? you should select columns .
try this
#Query("SELECT * FROM WardTransaction w WHERE w.id in (SELECT MAX(x.id)
FROM WardTransaction x WHERE w.id = x.id AND w.visit = :visit)")
This query is simpler and I think would get you what you want:
SELECT something
FROM sometable
Where something = someotherthing
ORDER BY sometable.id DESC
LIMIT 1
Basically it returns the results with the highest IDs at the top and grabs the first one.
I have following tables:
**visitors**
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| visitors_id | int(11) | NO | PRI | NULL | auto_increment |
| visitors_path | varchar(255) | NO | | | |
+---------------------+--------------+------+-----+---------+----------------+
**fedora_info**
+----------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| pid | varchar(255) | NO | PRI | | |
| owner_uid | int(11) | YES | | NULL | |
+----------------+--------------+------+-----+---------+-------+
First I looking for visitors_path that are related to specific pages by:
SELECT visitors_id, visitors_path
FROM visitors
WHERE visitors_path REGEXP '[[:<:]]fedora/repository/.*:[0-9]+$';
The above query return expected result.
now .*:[0-9]+ in above query referred to pid in second table. now I want know count of result in above query grouped by owner_uid in second table.
How can I JOIN this tables?
EDIT
sample data:
visitors
+-------------+---------------------------------+
| visitors_id | visitors_path |
+-------------+---------------------------------+
| 4574 | fedora/repository/islandora:123 |
| 4575 | fedora/repository/islandora:123 |
| 4580 | fedora/repository/islandora:321 |
| 4681 | fedora/repository/islandora:321 |
| 4682 | fedora/repository/islandora:321 |
| 4704 | fedora/repository/islandora:321 |
| 4706 | fedora/repository/islandora:456 |
| 4741 | fedora/repository/islandora:456 |
| 4743 | fedora/repository/islandora:789 |
| 4769 | fedora/repository/islandora:789 |
+-------------+---------------------------------+
fedora_info
+-----------------+-----------+
| pid | owner_uid |
+-----------------+-----------+
| islandora:123 | 1 |
| islandora:321 | 2 |
| islandora:456 | 3 |
| islandora:789 | 4 |
+-----------------+-----------+
Expected result:
+-----------------+-----------+
| count | owner_uid |
+-----------------+-----------+
| 2 | 1 |
| 4 | 2 |
| 3 | 3 |
| 2 | 4 |
| 0 | 5 |
+-----------------+-----------+
I suggest you to normalize your database. When inserting rows in visitors extract pid in the front end language and put it in a separate column (e.g. fi_pid). Then you can join it easily.
The following query might work for you. But it'll be little cpu intensive.
SELECT
COUNT(a.visitors_id) as `count`,
f.owner_uid
FROM (SELECT visitors_id,
visitors_path,
SUBSTRING(visitors_path, ( LENGTH(visitors_path) -
LOCATE('/', REVERSE(visitors_path)) )
+ 2) AS
pid
FROM visitors
WHERE visitors_path REGEXP '[[:<:]]fedora/repository/.*:[0-9]+$') AS `a`
JOIN fedora_info AS f
ON ( a.pid = f.pid )
GROUP BY f.owner_uid
Following query returns expected result, but its very slow Query took 9.6700 sec
SELECT COUNT(t2.pid), t1.owner_uid
FROM fedora_info t1
JOIN (SELECT TRIM(LEADING 'fedora/repository/' FROM visitors_path) as pid
FROM visitors
WHERE visitors_path REGEXP '[[:<:]]fedora/repository/.*:[0-9]+$') t2 ON t1.pid = t2.pid
GROUP BY t1.owner_uid
I am trying to get the correct formatting of results back from a Mysql query. When I ignore the NULL values, the formatting is correct, but when I allow null values to be included, my results are messed up.
I have the following query I am using:
select name,suite,webpagetest.id,MIN(priority) AS min_pri
FROM webpagetest,comparefileerrors
WHERE vco="aof" AND user="1" AND calibreversion="9"
AND webpagetest.id=comparefileerrors.id
AND comparefileerrors.priority IS NOT NULL
GROUP BY id
ORDER BY coalesce(priority,suite,name) ASC;
This returns the expected output:
+-----------------------------+-----------------------------+-------+---------+
| name | suite | id | min_pri |
+-----------------------------+-----------------------------+-------+---------+
| set_get_status | shortsRepairDB_2009.1_suite | 6193 | 0 |
| u2uDemo | shortsRepairDB_2009.1_suite | 6195 | 0 |
| change_sets | shortsRepairDB_2009.1_suite | 6194 | 0 |
| bz1508_SEGV_password | NULL | 6185 | 1 |
| assign_short_AND_user_info | shortsRepairDB_2009.1_suite | 6198 | 2 |
| bz1273_cmdline_execplussvdb | NULL | 6203 | 2 |
| bz1747_bad_lvsf | NULL | 36683 | 3 |
+-----------------------------+-----------------------------+-------+---------+
However, sometimes the priority values will not be set. If this is the case, I want the database to treat the priority as if it had an extremely high priority, so that the values with a null-priority are at the very bottom. I can not set the priority ahead of time (using a default value), but for the purposes of the sort, is it possible to do this?
Currently, if I issue the following command,
select name,suite,webpagetest.id,MIN(priority) AS min_pri
FROM webpagetest,comparefileerrors
WHERE vco="aof" AND user="1" AND calibreversion="9"
AND webpagetest.id=comparefileerrors.id
GROUP BY id
ORDER BY coalesce(priority,suite,name) ASC;
I get output like the following:
| name | suite | id | min_pri |
+-----------------------------+-------+-------+---------+
| bz1747_bad_lvsf | NULL | 36683 | 1 |
| NEC_Dragon.query | NULL | 36684 | NULL |
| avago_hwk_elam0_asic | NULL | 6204 | NULL |
| bz1273_cmdline_execplussvdb | NULL | 6203 | 2 |
| bz1491_query_server_crash | NULL | 6188 | NULL |
| bz1493_export_built_in_prop | NULL | 6186 | NULL |
+-----------------------------+-------+-------+---------+
6 rows in set (0.68 sec)
Here I have lost the formatting I had before. I would like the formatting to be as follows:
| name | suite | id | min_pri |
+-----------------------------+-------+-------+---------+
| bz1747_bad_lvsf | NULL | 36683 | 0 |
| NEC_Dragon.query | NULL | 36684 | 0 |
| avago_hwk_elam0_asic | NULL | 6204 | 1 |
| bz1273_cmdline_execplussvdb | NULL | 6203 | 2 |
| bz1491_query_server_crash | NULL | 6188 | NULL |
| bz1493_export_built_in_prop | NULL | 6186 | NULL |
+-----------------------------+-------+-------+---------+
6 rows in set (0.68 sec)
Hopefully I've explained this well enough that someone can understand what I want here.
Thanks for looking!
if you don't want to use sentinel value, i.e. ORDER BY COALESCE(priority, 99999); use:
select * from x
order by
case
when priority is not null then 1 /* non-nulls, first */
else 2 /* nulls last */
end,
priority
or you can take advantage of the fact that mysql boolean expression results to either 1 or 0:
select * from x
order by priority is null, priority
or if you're using postgresql:
select * from x order by priority nulls first
alternatively:
select * from x order by priority nulls last
Sounds like you want MIN(IFNULL(priority, 99999)). See the documentation for the IFNULL() function.