Group by date and sum of all unique values - mysql

I'm trying to write a SQL query for this report to group by date and also get the count of all unique values. The problem I have is that I do not know how many unique values I will have ahead of time.
Sample Table:
+--------+--------+
| Date | Name |
+--------+--------+
| 1/1/18 | John |
| 1/1/18 | John |
| 1/1/18 | Sylvia |
| 1/2/18 | Sylvia |
+--------+--------+
This is what I tried but it requires me to know that John and Sylvia exist in the table. What is the workaround if there were 50,000 unique names without having to type out all the CASE statement.
SELECT
date,
SUM(CASE WHEN name='John' THEN 1 ELSE 0 END) AS John,
SUM(CASE WHEN name='Sylvia' THEN 1 ELSE 0 END) AS Sylvia
FROM myTable
GROUP BY date;
Expected output:
+--------+------+--------+-----+
| Date | John | Sylvia | ... |
+--------+------+--------+-----+
| 1/1/18 | 2 | 1 | ... |
| 1/2/18 | 0 | 1 | ... |
+--------+------+--------+-----+

A simple
SELECT date, name, count(*) FROM myTable GROUP BY date, name;
should work

You can achieve this using Dynamic PIVOT.
Declare #sql nvarchar(max)
set #sql = 'select *
from (select *,count(name)cnt from #mytable group by name,date)A
PIVOT
( sum(cnt)
FOR name
in ( '+stuff(( select distinct ', '+ name from #mytable group by name for xml path('')),1,1,'')+')
)as PIVOTTABLE'
EXECUTE sp_executesql #sql;
This link may help you MySQL Select Query to generate dynamic column Result

Related

How Do Efficiently I Convert Dynamic Rows to Column in MySQL

I'm looking for an efficient way to convert rows to columns in the SQL server, I heard that PIVOT is not very fast, and I need to deal with a lot of records.
I tried following on this Efficiently convert rows to columns in sql server but still not solved with my below example
This is my example: (updated)
-----------------------------------------------
| Id | Value | ColumnName | Submission_Id |
-----------------------------------------------
| 1 | John | FirstName | 1 |
| 2 | 2.4 | Amount | 1 |
| 3 | ZH1E4A | PostalCode | 1 |
| 4 | Fork | LastName | 1 |
| 5 | 857685 | AccountNumber | 1 |
| 6 | Donny | FirstName | 2 |
| 7 | 2.7 | Amount | 2 |
| 8 | ZH1E4C | PostalCode | 2 |
| 9 | Yen | LastName | 2 |
| 10 | 857686 | AccountNumber | 2 |
-----------------------------------------------
This is my expected result:
---------------------------------------------------------------------
| FirstName |Amount| PostalCode | LastName | AccountNumber |
---------------------------------------------------------------------
| John | 2.4 | ZH1E4A | Fork | 857685 |
| Donny | 2.7 | ZH1E4C | Yen | 857686 |
---------------------------------------------------------------------
How can I build the result?
You need to group them by their associative key, which you informed:
SELECT
MAX(CASE ColumnName WHEN 'FirstName' THEN Value END) AS FirstName,
MAX(CASE ColumnName WHEN 'Amount' THEN Value END) AS Amount,
MAX(CASE ColumnName WHEN 'PostalCode' THEN Value END) AS PostalCode,
MAX(CASE ColumnName WHEN 'LastName' THEN Value END) AS LastName,
MAX(CASE ColumnName WHEN 'AccountNumber' THEN Value END) AS AccountNumber
FROM table
GROUP BY submission_id
;
GROUP BY enforces that there is single row for each unique submission_id, MAX selects the most descendant value of the expression for that particular group key (it is assumed to be singular so aggregate type should not matter), and finally CASE is filtering the Value by ColumnName.
WITH
indata(Id,Value,ColumnName) AS (
SELECT 1,'John' ,'FirstName'
UNION ALL SELECT 2,'2.4' ,'Amount'
UNION ALL SELECT 3,'ZH1E4A' ,'PostalCode'
UNION ALL SELECT 4,'Fork' ,'LastName'
UNION ALL SELECT 5,'857685' ,'AccountNumber'
UNION ALL SELECT 6,'Donny' ,'FirstName'
UNION ALL SELECT 7,'2.7' ,'Amount'
UNION ALL SELECT 8,'ZH1E4C' ,'PostalCode'
UNION ALL SELECT 9,'Yen' ,'LastName'
UNION ALL SELECT 10,'857686','AccountNumber'
)
,
-- need to get a grouping column, one that
-- changes every time we encounter a 'FirstName
-- add a counter that is at 1 for FirstName
-- otherwise at 0, and build a running sum...
w_session_id AS (
SELECT
SUM(CASE ColumnName WHEN 'FirstName' THEN 1 END)
OVER(ORDER BY id) AS sessid
, *
FROM indata
)
-- now un-pivot manually
SELECT
sessid AS id
, MAX(CASE ColumnName WHEN 'FirstName' THEN value END) AS FirstName
, MAX(CASE ColumnName WHEN 'Amount' THEN value END) AS Amount
, MAX(CASE ColumnName WHEN 'PostalCode' THEN value END) AS PostalCode
, MAX(CASE ColumnName WHEN 'LastName' THEN value END) AS LastName
, MAX(CASE ColumnName WHEN 'AccountNumber' THEN value END) AS AccountNumber
FROM w_session_id
GROUP BY sessid;
-- out id | FirstName | Amount | PostalCode | LastName | AccountNumber
-- out ----+-----------+--------+------------+----------+---------------
-- out 1 | John | 2.4 | ZH1E4A | Fork | 857685
-- out 2 | Donny | 2.7 | ZH1E4C | Yen | 857686

mySQL SELECT based on its result

I have a table like this:
| id | parentID | columnName |
|----|----------|-------------------|
| 1 | 2 | John |
| 2 | 0 | Task name |
| 3 | 4 | John |
| 4 | 0 | Task another name |
I want to get John task, but also with its name so I'm wondering if it is any way to do it in single MySQL query?
SELECT id, parentID, person FROM table WHERE columnName = "John"
will result in user task, but I want to know how this task is named.
You seem to be looking for a self-join:
select
t.id,
t.parentid,
t.columnname person,
t1.columnname task
from mytable t
left join mytable t1 on t1.id = t.parentid
where t.person = 'John'

MySQL - sort by certain last string character

I'm trying to sort by a certain character on a string, for example,
before:
+----+---------+
| id | name |
+----+---------+
| 1 | red |
| 2 | red-a |
| 3 | red-xy |
| 4 | blue |
| 5 | blue-a |
| 6 | blue-xy |
+----+---------+
after:
+----+---------+
| id | name |
+----+---------+
| 4 | blue |
| 1 | red |
| 5 | blue-a |
| 2 | red-a |
| 6 | blue-xy |
| 3 | red-xy |
+----+---------+
are there any ways to categorize based on -a or -xy using ORDER BY
Thank you in advance.
SELECT
CASE
WHEN RIGHT(`name`,LENGTH(`name`)-INSTR(`name`,'-')) = `name` THEN ''
ELSE RIGHT(`name`,LENGTH(`name`)-INSTR(`name`,'-'))
END AS `suffix`,
`name`
FROM
`table1`
ORDER BY
`suffix`, `name`
If no suffix is found, it will put the record in the first row set.
Caveat: the first dash is used to separate the word from the prefix.
This will do what you're looking for. Wouldn't like to promise great performance if you had a lot of rows though:
select id, name from
(
select id,
name,
if (substring_index(name,'-', -1) = name, '', substring_index(name,'-', -1)) as grouping
from Table1
order by grouping, name
) as subTable
SQLFiddle here
[EDIT] Actually, that can be simplified to a single select with :
select id,
name
from Table1
order by if (substring_index(name,'-', -1) = name, '', substring_index(name,'-', -1)), name
These queries are more readable and this is probably the easiest way to sort on a suffix
SELECT
*
, IF (LOCATE('-', name) = 0
, 0
, LENGTH(SUBSTRING_INDEX(name, '-', -1))
)
suffix_length
FROM
Table1
ORDER BY
suffix_length
;
SELECT
*
FROM
Table1
ORDER BY
IF (LOCATE('-', name) = 0
, 0
, LENGTH(SUBSTRING_INDEX(name, '-', -1))
)
;
See demo http://sqlfiddle.com/#!9/92b63/41

MySQL Select COUNT(*) and Row with Max Date

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

How to select from select where group by in MySQL?

I am trying to select and group by column, but keeping other column with all rows. I get error message that #1242 - Subquery returns more than 1 row.
My table
The result I want
Bellow is my query :
SELECT name FROM table WHERE (SELECT pro_id FROM table GROUP BY pro_id)
Try this query:
SELECT Pro_id
, GROUP_CONCAT(Name SEPARATOR ', ') AS Name
FROM MyTable
GROUP BY Pro_ID;
Result:
| PRO_ID | NAME |
-------------------------
| 1 | john, sandra |
| 2 | jeo |
| 3 | bruno, piter |
See this SQLFiddle
You cannot "merge cells" in a SQL result set as you do in your example. The best you could do is either "concat" several values in one "cell" using the GROUP_CONCAT function, or sort the rows to group common values together using ORDER BY clause.
Considering that, you need a very basic SELECT ... ORDER BY statement:
SELECT name, pro_id FROM table ORDER BY pro_id
Producing:
+---------+--------+
| NAME | PRO_ID |
+---------+--------+
| john | 1 |
| sandra | 1 |
| jeo | 2 |
| bruno | 3 |
| piter | 3 |
+---------+--------+
See http://sqlfiddle.com/#!2/6ea716/2