postgresql returns null but mysql doesn't - mysql

I have an application for which I am migrating from Mysql to Psql.
I have three tables t1,t2,t3 described below . Table t3 will always have a entry as long as user is available , but both table t1 and t2 may or may not have entry if the user doesn't create DB in his account.
While executing q1 in mysql , I get result set containing values fetched from table t3, even if t1 and t2 doesn't have entry , but it returns null in psql . So I've written two queries pq1 and pq2 to be equivalent to q1 . What is the reason that mysql doesn't return null values but psql does? Is there any better solution to this than breaking down into two queries for psql ?
mysql query (q1)
select
COALESCE(sum(dr.NO_OF_QT),0),
ur.NO_OF_USERS, ur.NO_OF_DB, 0,
COALESCE(sum(dr.NO_OF_SM),0),
COALESCE(sum(dr.NO_OF_RPTS),0)
from DataBaseProps dr
left join DataBaseDetails db on dr.DB_ID=db.ID and db.STATUS=1
left join UserProps ur on db.OWNER_UID=ur.USER_UID
where ur.USER_UID='USER_UID'
Psql-query_1 (pq1)
select
COALESCE(sum(dr.NO_OF_QT),0),
0,0, 0,
COALESCE(sum(dr.NO_OF_SM),0),
COALESCE(sum(dr.NO_OF_RPTS),0)
from DataBaseProps dr
left join DataBaseDetails db on dr.DB_ID=db.ID and db.STATUS=1
left join UserProps ur on db.OWNER_UID=ur.USER_UID
where ur.USER_UID='USER_UID'
psql-query_2(pq2)
select NO_OF_USERS,NO_OF_DB from UserProps
where USER_UID='USER_UID'
Table 1 DataBaseProps (t1)
desc DataBaseProps >
+-----------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------+------+-----+---------+-------+
| DB_ID | bigint(19) | NO | PRI | NULL | |
| NO_OF_RPTS | int(10) | YES | | 0 | |
| NO_OF_QT | int(10) | YES | | 0 | |
| NO_OF_SM | int(10) | YES | | 0 | |
+-----------------+------------+------+-----+---------+-------+
Table 2 - DataBaseDetails(t2)
desc DataBaseDetails>
+-------------------+--------------+------+-----+-----------------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+-----------------+-------+
| ID | bigint(19) | NO | PRI | NULL | |
| NAME | varchar(50) | NO | | NULL | |
| STATUS | int(10) | NO | | 1 | |
| OWNER_UID | bigint(19) | NO | | NULL | |
+-------------------+--------------+------+-----+-----------------+-------+
Table 3 UserProps(t3)
desc UserProps>
+-----------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------+------+-----+---------+-------+
| USER_UID | bigint(19) | NO | PRI | NULL | |
| NO_OF_DB | int(10) | YES | | 0 | |
| NO_OF_USERS | int(10) | YES | | 0 | |
+-----------------+------------+------+-----+---------+-------+

Related

Debugging a rather difficult/complex MySQL query

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.

MYSQL Joins where conditions may be null

I'm having an issue with a query using INNER JOIN.
I have two tables. I need the department name and all three approvers. If any of the approvers are NULL, I need that displayed also.
mysql> desc department;
+-------------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+----------------+
| id | int(8) | NO | PRI | NULL | auto_increment |
| departmentName | tinytext | YES | | NULL | |
| primaryApprover | int(8) | YES | | NULL | |
| secondaryApprover | int(8) | YES | | NULL | |
| tertiaryApprover | int(8) | YES | | NULL | |
+-------------------+----------+------+-----+---------+----------------+
mysql> desc approver;
+------------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------+------+-----+---------+----------------+
| id | int(8) | NO | PRI | NULL | auto_increment |
| approverName | tinytext | YES | | NULL | |
| approverPosition | tinytext | YES | | NULL | |
| approverLogonId | tinytext | YES | | NULL | |
| approverEmail | tinytext | YES | | NULL | |
| isActive | tinyint(1) | YES | | NULL | |
+------------------+------------+------+-----+---------+----------------+
The following query works, but it does not give me data where the primary or secondary approver are NULL:
SELECT
a.departmentName as DEPARTMENT,
pa.approvername as PRIMARY,
sa.approvername as SECONDARY,
ta.approvername as TERTIARY
FROM
department as a
INNER JOIN
approver pa on a.primaryapprover=pa.id
INNER JOIN
approver sa on a.secondaryapprover = sa.id
INNER JOIN
approver ta on a.tertiaryapprover = ta.id
ORDER BY
a.departmentname;
Using this query, I get this result:
+--------------------------------+---------------------------+---------------------------+------------------------+
| DEPARTMENT | PRIMARY_APPROVER | SECONDARY_APPROVER | TERTIARY_APPROVER |
+--------------------------------+---------------------------+---------------------------+------------------------+
| Facilities | Washburn, Hoban | Cobb, Jayne | Reynolds, Malcomn |
| Personnel / HR | Frye, Kaylee | Serra, Inara | Book, Dariel |
+--------------------------------+---------------------------+---------------------------+------------------------+
2 rows in set (0.00 sec)
but should get this result:
+--------------------------------+---------------------------+---------------------------+------------------------+
| DEPARTMENT | PRIMARY_APPROVER | SECONDARY_APPROVER | TERTIARY_APPROVER |
+--------------------------------+---------------------------+---------------------------+------------------------+
| Business Office | NULL | Rample, Fanty | Niska, Adelei |
| Facilities | Washburn, Hoban | Cobb, Jayne | Reynolds, Malcomn |
| Personnel / HR | Frye, Kaylee | Serra, Inara | Book, Dariel |
| Technical Services | Tam, River | NULL | Tam, Simon |
+--------------------------------+---------------------------+---------------------------+------------------------+
4 rows in set (0.00 sec)
I'm not good at joins to begin with....what am I missing here?
Just use LEFT JOINS
SELECT
a.departmentName as DEPARTMENT,
pa.approvername as PRIMARY,
sa.approvername as SECONDARY,
ta.approvername as TERTIARY
FROM
department as a
LEFT JOIN
approver pa on a.primaryapprover=pa.id
LEFT JOIN
approver sa on a.secondaryapprover = sa.id
LEFT JOIN
approver ta on a.tertiaryapprover = ta.id
ORDER BY
a.departmentname;
INNER JOIN - keeps only records that match from both sides .
LEFT JOIN - keeps all the records from the left table, and only the record matching from the right table.
You can also use COALESCE to replace null values with a default value like '-1' or something.

Slow query and use of indexes in MySQL

I have the following query:
SELECT final_query.chr
, final_query.start
, final_query.end
, co.chr
, co.start
, co.end
, final_query.count
FROM (SELECT ed.chr
, ed.start
, ed.end
, case when e.bin1=ed.bin then e.bin2 else e.bin1 end AS target
, count
FROM (SELECT * FROM coordinates
WHERE chr="chr1" AND (start between 3960000 AND 4000000 OR end between 3960000 AND 4000000)
) ed
JOIN counts e ON (e.bin1 = ed.bin OR e.bin2=ed.bin)
SORT BY count LIMIT 1,20)
AS final_query
JOIN coordinates co ON final_query.target=co.bin;
and the output of EXPLAINED is:
+------+-------------+-------------+--------+---------------+---------+---------+-------+----------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------------+--------+---------------+---------+---------+-------+----------+------------------------------------+
| 1 | SIMPLE | e | ALL | bin1,bin2 | NULL | NULL | NULL | 30763816 | Using filesort |
| 1 | SIMPLE | coordinates | ref | PRIMARY,chr | chr | 22 | const | 4929 | Using index condition; Using where |
| 1 | SIMPLE | co | eq_ref | PRIMARY | PRIMARY | 22 | func | 1 | Using where |
+------+-------------+-------------+--------+---------------+---------+---------+-------+----------+------------------------------------+
What I am doing is to perform the following query of table coordinates, which has field chr indexed. So, in the subquery shown below, I filter those rows that match my conditions.
... (SELECT * FROM coordinates
WHERE chr="chr1" AND (start between 3960000 AND 4000000 OR end between 3960000 AND 4000000)
) ...
This table outputs field bin, also indexed. This field bin links with bin1 and bin2 both from table counts and indexed as well. So, here, what I want is to get all those rows in table counts having coordinates.bin in fields bin1 and bin2. Why in this step no index is used?
Besides of it, I would like to add an ORDER BY in my query, just before the LIMIT statement. But it slows too much my query. I don't know why, because it have to sort a maximum of 4000 rows...
How can I optimize my query?
My tables, from the DESCRIBE statement:
Table counts
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| bin1 | varchar(20) | NO | MUL | NULL | |
| bin2 | varchar(20) | NO | MUL | NULL | |
| count | float(6,2) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
Table coordinates
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| bin | varchar(20) | NO | PRI | NULL | |
| chr | varchar(20) | NO | MUL | NULL | |
| start | int(11) | NO | | NULL | |
| end | int(11) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+

Query returning multiple objects when only one is expected

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.

mysql query: data from last three months

I am doing a query that is retrieving some data from the past three months, the only problem is that some of the data I am getting doesn't have entries in certain months. Since they have no entries I'd like to mark that month as 0.
My first thought was the create a temp table and left join the labels that I need out of it. But that hasnt been successful.
Can anyone think of a way to do this?
Example: I want the last 3 months of Data and I am getting
'Component', 1325.1988
'Component', 554.1652
'Component', 103.6668
'Development', 203.4163
'Development', 59.4500
'Development', 19.7498
'Flash Assets', 285.5334
'Flash Assets', 302.1501
'Flash Assets', 61.1836
'Release', 0.6000
'Release', 2.3666
'Repackage', 416.2169
'Repackage', 5195.0839
'Repackage', 4.5667
'Source Diff', 1.9000
Where 'Source Diff' and 'Release' don't have 3 entries.
Thanks
Query
SELECT bt.name as 'Labels',
SUM(TIME_TO_SEC(TIMEDIFF(bs.eventtime, b.submittime))/60) AS 'Data'
FROM builds b JOIN buildstatuses bs ON bs.buildid = b.id JOIN buildtypes bt
ON bt.id = b.buildtype WHERE DATE(b.submittime)
BETWEEN DATE_SUB(CURDATE(), INTERVAL 2 MONTH) AND DATE(CURDATE())
AND bs.status LIKE 'Started HANDLER' AND b.buildtype != 11
AND b.buildtype != 5 AND b.buildtype != 4 GROUP BY bt.name, MONTH(b.submittime);
Table Schema
builds
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| submittime | datetime | NO | | NULL | |
| buildstatus | int(11) | NO | | NULL | |
| buildtype | varchar(20) | NO | | NULL | |
| buildid | int(11) | NO | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
buildtypes
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(200 | NO | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
buildstatuses
+------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| buildid | int(11) | NO | MUL | NULL | |
| eventtime | datetime | NO | | NULL | |
+------------+----------+------+-----+---------+----------------+
Here are some similar questions:
How to get values for every day in a month
Group by day and still show days without rows?
MySQL: filling empty fields with zeroes when using GROUP BY