MySql Query Building help needed - mysql

users table
id
name
company_id
1
user 1
1
2
user 2
2
3
user 3
1
How can I select all the company_id uniquely while ordering them by user name ?
I have tried below query in ONLY_FULL_GROUP_BY false mode
select
distinct `company_id`
from
`users`
order by
`name` DESC
But this messed up my ordering.

So based on your requirements you'd need to do something like:
select `company_id`
from
`users`
group by `company_id`
order by MAX(`name`) DESC
This should group company ids and order them based on the descending order of the last name that appears in that group.

Related

Group by - do not Group NULL and control record displayed

Similar to this question: group by not-null values
I'm trying to only group my records that have the column groupID not null:
+--+-------+------+-----+-----+----------+
|id|groupId|isMain|name |stars|created |
+--+-------+------+-----+-----+----------+
..1..abcd....1.....john....5...2018-06-01.
..2..NULL....0.....albert..3...2018-05-01.
..3..abcd....0.....clara...1...2018-06-01.
..4..NULL....0.....steph...2...2018-07-01.
With this query I'm able to group only those records where groupId is not null:
SELECT *, SUM(stars) as stars
FROM table AS
GROUP BY (case when `groupId` is null then id else `group` end)
ORDER BY created DESC
This gives me the result:
4..NULL....0.....steph...2...2018-07-01
3..NULL....0.....clara...6...2018-06-01
2..NULL....0.....albert..3...2018-05-01
I'm trying to select, for those records grouped, the ones that isMain is 1 but I have no clue how to achieve that.
I've tried playing with HAVING but that gives me a totally different result.
You could use CASE or IFNULL
but you should use proper aggregation function and group by columns clause eg:
select ifnull(groupID, id), name, sum(stars), max(created) as my_create
from table
group by ifnull(groupID, id), name
order by my_create

MySQL put a specific row at the top of the result

I'm doing a basic SQL select query which returns a set of results. I want a specific row which the entry "Fee" to be put at the top of the results, then the rest.
Something like:
SELECT * FROM tbl ORDER By Charges = Fee DESC, Charges DESC
Can anyone help?
You could try this :
SELECT * from tbl ORDER BY CASE WHEN Charges = 'Fee' THEN 0 ELSE 1 END, Charges DESC;
I think you'd have a use a UNION query. ORDER BY doesn't support this kind of thing by default as far as I know.
Something like this:
SELECT * FROM tbl WHERE Charges = 'Fee'
UNION
SELECT * FROM tbl ORDER BY Charges DESC
You would have to use ORDER BY with a FIELD attribute, which would then order by those first.
As I don't have your table definitions, I have throw one together here http://sqlfiddle.com/#!9/91376/13
For sake of it disappearing, the script pretty much consists of;
CREATE TABLE IF NOT EXISTS `tbl` (
`id` int(6) unsigned AUTO_INCREMENT,
`Name` char(6) not null,
`Charges` char(10) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tbl` (`Name`, `Charges`)
VALUES ('One', 'Fee'), ('Two', 'Charge'), ('Three', 'Charge'),
('Four', 'Charge'), ('Five', 'Fee'), ('Six', 'Fee'),
('Seven', 'Invoice'), ('Eight', 'Fee'), ('Nine', 'Invoice'),
('Ten', 'Invoice');
SELECT *
FROM tbl
ORDER BY FIELD(`Charges`, 'Charge') DESC
;
Which returns:
id Name Charges
2 Two Charge
3 Three Charge
4 Four Charge
1 One Fee
9 Nine Invoice
8 Eight Fee
7 Seven Invoice
6 Six Fee
5 Five Fee
10 Ten Invoice
So, to directly answer your question, your query would be;
SELECT *
FROM tbl
ORDER BY FIELD(Charges, 'Fee') DESC
edit : Viewable, sorted by Charges = Fee here : http://sqlfiddle.com/#!9/91376/15
SELECT * FROM tbl ORDER By FIELD(Charges, 'Fee') DESC
You can use something like the above. Where Charges is the field and fee the specific value. That way you can keep it simple.

SQL queries optimization

I'm having trouble optimizing some sql queries that take in account datetime fields.
First of all, my table structure is the following:
CREATE TABLE info (
id int NOT NULL auto_increment,
name varchar(20),
infoId int,
shortInfoId int,
text varchar(255),
token varchar(60),
created_at DATETIME,
PRIMARY KEY(id)
KEY(created_at));
After using explain on some of the simple queries I added the created_at key, that improved most of my simple queries performance. I'm having now trouble with the following query:
SELECT min(created_at), max(created_at) from info order by id DESC limit 10000
With this query I want to get the timespan between tha last 10k results.
After using explain I get the following results:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE info ALL NULL NULL NULL NULL 4 NULL
Any idea on how can I improve the performance of this query?
If you want to examine the first 10k rows ordered by id then you need to use a sub-query to achieve your goal:
SELECT MIN(created_at), MAX(created_at)
FROM (
SELECT created_at
FROM info
ORDER BY id DESC
LIMIT 10000
) tenK
The inner query gets the first 10k rows from the table, sorted by id (only the field created_at is needed). The outer table computes the minimum and maximum value of created_at from the results set generated by the inner query.
I didn't run an EXPLAIN on it but I think it says 'Using temporary' in the 'Extra' column (which is not good but you cannot do better for this request). However, 10,000 rows is not that much; it runs fast and the performance does not degrade as the table size increases.
Update:
Now I noticed this sentence in the question:
With this query I want to get the timespan between tha last 10k results.
If you want to get the value of created_at of the most recent row and the row that is 10k rows in the past then you can use two simple queries that use the index on created_at and run fast:
(
SELECT created_at
FROM info
ORDER BY id DESC
LIMIT 1
)
UNION ALL
(
SELECT created_at
FROM info
ORDER BY id DESC
LIMIT 9999,1
)
ORDER BY created_at
This query produces 2 rows, the first one is the value of created_at of the 10000th row in the past, the second one is the created_at of the most recent row (I assume created_at always grows).
SELECT min(created_at), max(created_at) from info order by id DESC limit 10000
The above query will give you one row containing the minimum and maximum created_at values from info table. Because it only returns 1 row, the order by and limit clauses don't come into play.
The 10000-th record from the end can be accessed with the order by & limit condition ORDER BY id DESC LIMIT 1 OFFSET 9999 (thanks #Mörre Noseshine for the correction)
So, we can write the intended query as follows:
SELECT
min_created_at.value,
max_created_at.value
FROM
(SELECT
created_at value
FROM info
ORDER BY id DESC
LIMIT 1 OFFSET 9999) min_created_at,
(SELECT
created_at value
FROM info
ORDER BY id DESC
LIMIT 1) max_created_at

Can this MySQL query be more efficient?

UPDATE Here's a sqlfiddle http://sqlfiddle.com/#!2/e0822/1/0
I have a MySQL database of apps (itunes_id), each app id has a comments field. To preserve a history, every time a comment is changed, a new row of data is added. In the query below, I just want a list of the latest entry (highest id) of every app (itunes_id).
Here are the headers of my db:
id (key and auto increment)
itunes_id
comments
date
This query is getting the latest entry for a given itunes_id. How can I make this query more efficient?
SELECT * FROM (
SELECT * FROM (
SELECT * FROM Apps
ORDER BY id DESC
) AS apps1
GROUP BY itunes_id
) AS apps2
LIMIT 0 , 25
This query uses a subquery which separately gets the maximum ID for every itunes_ID. The result of the subquery is then join back on the original table provided that it matches on two columns: itunes_ID and ID.
SELECT a.*
FROM Apps a
INNER JOIN
(
SELECT itunes_id, MAX(ID) max_id
FROM Apps
GROUP BY itunes_id
) b ON a.itunes_id = b.itunes_id AND
a.ID = b.max_ID
LIMIT 0, 25
For faster performance, create a compound column INDEX on columns itunes_ID and ID. EG,
ALTER TABLE Apps ADD INDEX (itunes_ID, ID)
For a similar approach, I use a "recent" boolean field to mark records containing the latest version. This requires an UPDATE query on every insert (deactivate the previous recent record), but allows for a quick select query. Alternatively, you could maintain two tables, one with the recent records, the other one with the history for each app.
EDIT: Maybe you can try a table similar to this:
id int not null auto_increment primary key
version int not null
main_id int null
recent boolean not null
app varchar(32) not null
comment varchar(200) null
You can use the column "main_id" to point to the record with version 1.
SELECT * FROM (
SELECT * FROM (
SELECT * FROM Apps
ORDER BY id DESC
) AS apps1
GROUP BY itunes_id
) AS apps2
LIMIT 0 , 25
will not select the oldest record (you cannot assume the generated key will always be the "oldest"). What you want is something like this:
SELECT * FROM (
SELECT * FROM (
SELECT * FROM Apps
where some_date = (select max(some_date) from Apps limit 1)
ORDER BY id DESC
) AS apps1
GROUP BY itunes_id
) AS apps2
LIMIT 0 , 25
I just want the latest entry (highest id) for a given app (itunes_id)
This will do it
SELECT MAX(id), comments FROM Apps WHERE itunes_id = "iid";
or
SELECT id, comments FROM Apps WHERE itunes_id = "iid" ORDER BY id DESC LIMIT 1;
Where iid is the itunes id for which you want the latest comment.
Make sure id and itunes_id are indexed in a composite index for maximum efficiency.

Get Last conversation row from MySQL database table

I have a database in MYSQL and it has chat table which looks like this.
I am using this query for fetching these records
SELECT * FROM (
SELECT * FROM `user_chats`
WHERE sender_id =2 OR receiver_id =2
ORDER BY id DESC
) AS tbl
GROUP BY sender_id, receiver_id
But my requirement is only 5,4 ID's records. basically my requirement id fetching last conversation in between 2 users. Here in between 2 & 3 user conversation has 2 records and we want only last one of them i.e. id = 5, here don't need id = 2.
So how we can write a query for that result?
SELECT
*
FROM
user_chats uc
WHERE
not exists (
SELECT
1
FROM
user_chats uc2
WHERE
uc2.created > uc.created AND
(
(uc.sender_id = uc2.sender_id AND uc.reciever_id = uc2.reciever_id) OR
(uc.sender_id = uc2.reciever_id AND uc.reciever_id = uc2.sender_id)
)
)
The following gets you latest record (assuming that the bigger id, the later it was created) meeting your criteria:
SELECT * FROM `user_chats`
WHERE (`sender_id` =2 AND `receiver_id` =3) OR (`sender_id` =3 AND `receiver_id` =2)
ORDER BY `id` DESC
LIMIT 1
which would be a good idea, if id is primary key and it rises along with rising value of created. Otherwise (if you are not sure that id rises when created rises) replace ORDER BY line with the following:
ORDER BY `created` DESC
Plus, in both cases, put proper indexes on: id (if it is your primary key, then there is no need to put additional index on it), sender_id and receiver_id (preferably composite index, meaning the single index for both columns), created (if you want to use ORDER BY created DESC instead of ORDER BY id DESC - otherwise there is no need for that).
try GROUP BY LEAST(sender_id, receiver_id), GREATEST(sender_id, receiver_id)