MySQL Select COUNT(*) and Row with Max Date - mysql

I am trying to select the COUNT(*) of deals grouped by seller along with their most recent product and the date that it was added. However, for some reason it seems to keep ordering the deals by creation date, ascending even when I try subqueries to prevent that. Here is an example table:
------------------------------------------------
| ID | Provider | URL | CreateDate |
------------------------------------------------
| 1 | Prov1 | http://ex.com/1 | 2015-05-10 |
| 2 | Prov1 | http://ex.com/2 | 2015-06-10 |
| 3 | Prov1 | http://ex.com/3 | 2015-07-10 |
| 4 | Prov2 | http://ex.com/4 | 2015-05-10 |
| 5 | Prov2 | http://ex.com/5 | 2015-06-10 |
------------------------------------------------
I am looking to return the following:
-----------------------------------------------------------
| ID | COUNT(*) | Provider | URL | CreateDate |
-----------------------------------------------------------
| 3 | 3 | Prov1 | http://ex.com/3 | 2015-07-10 |
| 5 | 2 | Prov2 | http://ex.com/5 | 2015-06-10 |
-----------------------------------------------------------
My current query is:
SELECT ID,COUNT(*),Provider,CreateDate,URL
FROM (SELECT * FROM products ORDER BY CreateDate DESC)
GROUP BY Provider;
But this doesn't seem to work. Does anyone have a suggestion?
EDIT
Thank you all for the great answers. What is extremely strange to me is that while they seem to work in a SQL fiddle, they fail on my database server. For example using the following on the server provides the following:
mysql> INSERT INTO products
-> (`ID`, `Provider`, `URL`, `CreateDate`)
-> VALUES
-> (1, 'Prov1', 'http://ex.com/1','2015-05-10'),
-> (2, 'Prov1', 'http://ex.com/2','2015-06-10'),
-> (3, 'Prov1', 'http://ex.com/3','2015-07-10'),
-> (4, 'Prov2', 'http://ex.com/4','2015-05-10'),
-> (5, 'Prov2', 'http://ex.com/5','2015-06-10')
-> ;
Query OK, 5 rows affected (0.06 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> SELECT ID,COUNT(*),Provider,CreateDate,URL
-> FROM (SELECT * FROM products ORDER BY CreateDate DESC) t1
-> GROUP BY Provider;
+------+----------+----------+---------------------+-----------------+
| ID | COUNT(*) | Provider | CreateDate | URL |
+------+----------+----------+---------------------+-----------------+
| 1 | 3 | Prov1 | 2015-05-10 00:00:00 | http://ex.com/1 |
| 4 | 2 | Prov2 | 2015-05-10 00:00:00 | http://ex.com/4 |
+------+----------+----------+---------------------+-----------------+
While running the same thing on SQL fiddle works as expected. Further the comment regarding the JOIN should work, but I am having the same issue with it returning unexpected results. My version of MySQL is 5.5.37-MariaDB-34.0. Any ideas on this?

SELECT p1.*, COUNT(*)
FROM products p1
JOIN
(
select provider, max(CreateDate) as m_date
from products
group by provider
) p2 on p1.provider = p2.provider and p1.CreateDate = p2.m_date

You need specify the field name where you want mysql return count content:
select count(*) as nro from table;
in your case:
SELECT ID,COUNT(*) as nro,Provider,CreateDate,URL
FROM (SELECT * FROM products ORDER BY CreateDate DESC)
GROUP BY Provider;
but for all solution you need create an alias for this part of query "SELECT * FROM products ORDER BY CreateDate DESC"
CREATE VIEW p AS (SELECT * FROM products ORDER BY CreateDate DESC);
and the second query:
SELECT ID,COUNT(*) as nro,Provider,CreateDate,URL
FROM (p)
GROUP BY Provider;
must be work.

I did it in SQL Server but the query in mysql works.
Create TABLE #Products(
Id int,
Provider varchar(50),
URL Varchar(300),
Createdate varchar(50)
)
INSERT INTO #Products
SELECT 1,'Prov1','http://ex.com/1 ','2015-05-10' UNION ALL
SELECT 2,'Prov1','http://ex.com/2 ','2015-06-10' UNION ALL
SELECT 3,'Prov1','http://ex.com/3 ','2015-07-10' UNION ALL
SELECT 4,'Prov2','http://ex.com/4 ','2015-05-10' UNION ALL
SELECT 5,'Prov2','http://ex.com/5 ','2015-06-10'
SELECT p1.id, p2.Provider,p2.m_date,p1.url, COUNT(*)
FROM #products p1
JOIN
(
select provider, max(CreateDate) as m_date
from #products
group by provider
) p2 on p1.provider = p2.provider and p1.CreateDate = p2.m_date
GROUP BY p1.id, p2.Provider,p2.m_date,p1.url

Related

I want find duplicate data? and display duplicate records only?

in my table having data like this
+-----+----------+
| sno | name |
+-----+----------+
| 101 | Raju |
| 102 | Raju |
| 103 | Santhosh |
| 104 | Santhosh |
| 105 | madhavi |
| 106 | suheel |
+-----+----------+
in that i want find dupliacte records and display sno(number) only
for example output should be like this
+-----+
| sno |
+-----+
| 101 |
| 102 |
| 103 |
| 104 |
+-----+
In a Derived table, get all the name values which have duplicates. To do that, we can GROUP BY on name and use HAVING to consider only those names, where COUNT(*) (total number of rows for that name) is more than 1.
Now, we can join back to the main table to get their respective sno values
SELECT
t.sno
FROM your_table t
JOIN (SELECT name
FROM your_table
GROUP BY name
HAVING COUNT(*) > 1) dt
ON dt.name = t.name
Here is a MySQL 8+ way of doing this:
SELECT sno
FROM
(
SELECT t.*, COUNT(*) OVER (PARTITION BY name) cnt
FROM yourTable t
) t
WHERE cnt > 1;
You can try using correlated subquery
select sno from tablename a
where name in (select 1 from tablename b where a.name=b.name having count(name)>1 )

MySQL 5.7 group by latest record

Very simple select, but I'm confused:
create table users (id int, type int);
insert into users values(1, 100), (2, 101), (3, 100);
mysql> select * from users;
+------+------+
| id | type |
+------+------+
| 1 | 100 |
| 2 | 101 |
| 3 | 100 |
+------+------+
I want to get the result:
+------+------+
| id | type |
+------+------+
| 3 | 100 |
| 2 | 101 |
+------+------+
My query is:
MySQL version 5.7 returns:
mysql> select * from (select * from users order by id desc) as t group by type;
+------+------+
| id | type |
+------+------+
| 1 | 100 |
| 2 | 101 |
+------+------+
At MySQL 5.5 it works as expected. sql_mode is NULL
Thanks for the replies. Extended table to have more clear results:
create table users (id int, type int, level int);
insert into users values(1, 100, 1000), (2, 101, 1001), (3, 100, 1002);
mysql> select max(id), type, level from users group by type;
+---------+------+-------+
| max(id) | type | level |
+---------+------+-------+
| 3 | 100 | 1000 | <- expected 1002
| 2 | 101 | 1001 |
+---------+------+-------+
This works:
mysql> SELECT t1.* FROM users t1 INNER JOIN (SELECT type, MAX(id) AS max_id FROM users GROUP BY type) t2 ON t1.type = t2.type AND t1.id = t2.max_id ORDER BY id DESC;
+------+------+-------+
| id | type | level |
+------+------+-------+
| 3 | 100 | 1002 |
| 2 | 101 | 1001 |
+------+------+-------+
You should use an aggregation function and group by
select max(id), type
from users
group by type;
order by id desc
the result in mysql5.5 not based on group by is casual .. (this beahvior is deprecated in sql and not allowed in default sql_mode in 5.7)
For completeness I can offer the following query which would work if you wanted to return entire records with an arbitrary number of columns.
SELECT t1.*
FROM users t1
INNER JOIN
(
SELECT type, MAX(id) AS max_id
FROM users
GROUP BY type
) t2
ON t1.type = t2.type AND
t1.id = t2.max_id
GROUP BY without aggregation is supported in very few SQL dialects (perhaps even only MySQL); the fact that it worked as you expected in 5.5 was (for the most part) just convenient, not guaranteed by MySQL. Officially, it grabs an effectively random value (encountered from the rows of the group) from the non-grouped, non-aggregated fields.

Select unique record from two tables when column count is not same in MySQL

I have two tables, both tables has same column but one table has extra columns. I need to select unique record from both of them on the basis of a column i.e. profilelink in below example exists in both table.
I tried union in below way:
SELECT * FROM t1
UNION
SELECT * FROM t2
but I think it is check for all columns in the table and then fetch out the result but in my case column count is not same in both tables and I need to fetch record on the basis of one column only. So can someone help me in this query.
Table structure is as follows
Table 1
id | name | marks | profilelink
1 | a | 10 | http://example.com/a
2 | b | 20 | http://example.com/b
3 | c | 30 | http://example.com/c
4 | d | 40 | http://example.com/d
Table 2
id | name | marks | Division | Result | profilelink
1 | e | 10 | I | Pass | http://example.com/e
2 | f | 20 | II | Pass | http://example.com/f
3 | b | 30 | III | Pass | http://example.com/b
4 | c | 40 | IV | Fail | http://example.com/c
Expected Result would be
id | name | marks | Division | Result | profilelink
1 | a | 10 | null | null | http://example.com/a
2 | d | 40 | null | null | http://example.com/a
3 | e | 10 | I | Pass | http://example.com/e
4 | f | 20 | II | Fail | http://example.com/f
In the above example profilelink is common in both tables. Result sequence doesn't matter.
Ok. You can select the specific columns names of each table but with the same order, like this:
SELECT t1_id AS id
, t1_name AS name
FROM t1
UNION
SELECT t2_id
, t2_name
FROM t2
As you told in question, you want to get records from both table which are not duplicate. Below query may help you out.
SELECT id, name, marks, profilelink, count(id) as count FROM
(SELECT id, name, marks, profilelink
from table1
UNION
SELECT id, name, marks, profilelink from table2) A
GROUP BY profilelink where count = 1
Subquery will get records from both tables and count records based on profileurl.
i had try and get all column as result with this Query :
SELECT `id`, `name`, `marks`, `Division`, `Result`, `profilelink`
FROM table_b
UNION
SELECT `id`, `name`, `marks`,'' as 'Division','' as 'Result', `profilelink`
FROM table_a
Output & Example :
Please Try This Query :
SELECT `id`, `name`, `marks`, `Division`, `Result`, `profilelink` from (
SELECT `id`, `name`, `marks`, `Division`, `Result`, `profilelink`, count(id) as count from (
SELECT `id`, `name`, `marks`, `Division`, `Result`, `profilelink`
FROM table_b
UNION
SELECT `id`, `name`, `marks`,null as 'Division',null as 'Result', `profilelink`
FROM table_a )tbl group by name ) tbl_Val where count=1 ;

Can't create view with query containing subquery, what can I do instead?

I want to create a view with for a table in my database so I can use it in joins more easily. The table UserSettings look like this:
mysql> desc UserSettings; -- cut the output for readability
+-----------+---------------------
| Field | Type
+-----------+---------------------
| userID | bigint(20) unsigned
| timestamp | timestamp
| settingID | text
| setting | text
+-----------+---------------------
4 rows in set (0.00 sec)
In it we store every update of a users settings. Now I want to create a view for this table with the latest settings for each user. So I want the view to contain the rows marked with *:
mysql> select * from UserSettings;
+--------+---------------------+-----------------------+---------+
| userID | timestamp | settingID | setting |
+--------+---------------------+-----------------------+---------+
| 123 | 2014-04-16 12:08:30 | settings.warnings_off | 1 |
| 123 | 2014-04-16 12:18:56 | settings.warnings_off | 0 |
| 123 | 2014-04-17 06:42:08 | settings.warnings_off | 1 |
| 123 | 2014-04-17 06:49:08 | settings.warnings_off | 0 | *
| 911 | 2014-04-17 06:49:33 | settings.warnings_off | 1 | *
| 123 | 2014-04-17 10:04:12 | settings.test | fooo |
| 123 | 2014-04-17 10:04:22 | settings.test | bar | *
| 911 | 2014-04-17 10:07:40 | settings.test | bar | *
+--------+---------------------+-----------------------+---------+
8 rows in set (0.00 sec)
I can do this with a query containing a subquery:
mysql> SELECT us.*
-> FROM UserSettings us INNER JOIN (
-> SELECT userID, settingID, max(timestamp) as timestamp
-> from UserSettings
-> group by userID, settingID) ts ON ts.userID = us.userID AND
-> ts.settingID = us.settingID AND ts.timestamp = us.timestamp;
+--------+---------------------+-----------------------+---------+
| userID | timestamp | settingID | setting |
+--------+---------------------+-----------------------+---------+
| 123 | 2014-04-17 10:04:22 | settings.test | bar |
| 123 | 2014-04-17 06:49:08 | settings.warnings_off | 0 |
| 911 | 2014-04-17 10:07:40 | settings.test | bar |
| 911 | 2014-04-17 06:49:33 | settings.warnings_off | 1 |
+--------+---------------------+-----------------------+---------+
4 rows in set (0.00 sec)
However I get the error ERROR 1349 (HY000): View's SELECT contains a subquery in the FROM clause when using the above query in CREATE VIEW CurrentUserSettings AS <THE QUERY ABOVE>.
Is there a way to create a view of the UserSettings table that show the same data as the query above? Perhaps using HAVING in some way?
To expand my comment above:-
CREATE VIEW latest_timestamp AS SELECT userID, settingID, max(timestamp) as timestamp
from UserSettings
group by userID, settingID;
CREATE VIEW latest_timestamp AS SELECT us.*
FROM UserSettings us
INNER JOIN latest_timestamp ts
ON ts.userID = us.userID AND
ts.settingID = us.settingID
AND ts.timestamp = us.timestamp;
Another option is to (ab)use the GROUP_CONCAT function. I am not that keen on doing this and there are issues if the fields can contain NULL, plus is entails converting integers to character fields.
Assuming the fields you want are called field1, field2 and field3 then:-
SELECT us.userID,
us.settingID,
SUBSTRING_INDEX(GROUP_CONCAT(us.field1 ORDER BY timestamp DESC SEPARATOR '|~|~'), '|~|~', 1),
SUBSTRING_INDEX(GROUP_CONCAT(us.field2 ORDER BY timestamp DESC SEPARATOR '|~|~'), '|~|~', 1),
SUBSTRING_INDEX(GROUP_CONCAT(us.field3 ORDER BY timestamp DESC SEPARATOR '|~|~'), '|~|~', 1)
FROM UserSettings us
GROUP BY us.userID, us.settingID;
This works by concatenating all the values for a field up into one, ordered by the timestamp descending and with a separator between them (something that will not be in any of the fields), then using SUBSTRING_INDEX to get the field up to the first delimiter.
Like Kickstart advises, do like this
First, create the subquery in a view
CREATE VIEW subqueryview as
SELECT userID, settingID, max(timestamp) as timestamp
from UserSettings group by userID, settingID;
Then you can create the view using the view.
CREATE VIEW finalview AS
SELECT us.*
FROM UserSettings us
-- Here you now use the view created.
INNER JOIN subqueryview ts
ON ts.userID = us.userID
AND ts.settingID = us.settingID
AND ts.timestamp = us.timestamp;

Mysql Group by clause help

$construct = "SELECT * FROM mytable GROUP BY nid HAVING nid>1";
mytable:
+----+----------+
| id | nid |
+----+----------+
| 1 | null |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | 3 |
| 8 | 3 |
| 9 | 4 |
| 10 | 4 |
-----------------
How do i GROUP BY nid except nid=1? This is a brief example but with my code i am not getting the desired results. Is the query correct for what i am trying to accomplish?
How about this:
SELECT * FROM mytable WHERE nid != 1 ORDER BY nid
GROUP BY causes an aggregate query which you can only sensibly use with an aggregation function. For example, SELECT COUNT(*), nid GROUP BY nid would give you the counts of rows with a given nid.
Update: Not sure I'm understanding you, but how about this then:
(SELECT * FROM mytable WHERE nid = 1 UNION SELECT * FROM mytable WHERE nid != 1 GROUP BY nid) ORDER BY nid
I'm not sure it makes sense to mix aggregate and non-aggregate queries, though -- on the aggregate side you'll just end up with an indeterminate representative row of that group.
SELECT count(*), nid FROM mytable where nid <> 1 GROUP BY nid;
or
SELECT count(*), nid FROM mytable where nid != 1 GROUP BY nid;
Not sure if you are using Oracle or MySQL.
…
GROUP BY CASE nid WHEN 1 THEN -id ELSE nid END
…