I have the following table:
id | billingno | location
-------------------------
1 | 9999999 | Toronto
2 | 9999999 | Toronto
3 | 7777777 | Toronto
4 | 7777777 | Quebec
I need a query that would generate me something that looked like this:
location | total | display
--------------------------
Toronto | 3 | 9999999 - 2, 7777777 - 1
Quebec | 1 | 7777777 - 1
So, it groups by location, displays the total number of billingno's for that location, and then the display column lists each billingno and how many times they were in that location. I have been trying to write this for some time, my closest attempt is this:
SELECT location, COUNT(*) AS total, GROUP_CONCAT(DISTINCT CAST(CONCAT(CONVERT(billingno,CHAR(16)), ' - ', THIS_COUNT_PART_FOR_EACH_LOCATION_IN_DISPLAY_DOESNT_WORK)AS CHAR)
SEPARATOR ' - ') AS display
FROM table GROUP BY location
ORDER BY COUNT(*) DESC
It gives me everything I need except I cannot for the life of me figure out how to count the number of each billingno's under display. If I use COUNT() it gives me an error about grouping. Please help!
Oh, I also had to use the convert to char so it would show up as text and not a BLOB in phpMyAdmin. Thanks again!
Sample data:
create table location (
id int,
billingno varchar(10),
location varchar(10)
);
insert into location
select 1, '9999999', 'Toronto' union
select 2, '9999999', 'Toronto' union
select 3, '7777777', 'Toronto' union
select 4, '7777777', 'Quebec' ;
Query:
select
location,
sum(qty) as total,
group_concat(concat(billingno, ' - ', cast(qty as char(7)))
order by qty desc separator ', '
) as display
from (
select billingno, location, count(*) as qty
from location
group by billingno, location
) t
group by location
order by total desc
Result:
location total display
Toronto 3 9999999 - 2, 7777777 - 1
Quebec 1 7777777 - 1
How about this,
SELECT table.location,
SUM(LocationCount) AS Total,
GROUP_CONCAT(CAST(CONCAT(CONVERT(billingno,CHAR(16)), ' - ', THIS_COUNT_PART_FOR_EACH_LOCATION_IN_DISPLAY_DOESNT_WORK)AS CHAR)
SEPARATOR ' - ') AS display
FROM table
LEFT JOIN
(SELECT location , COUNT(id) AS LocationCount
FROM table
GROUP BY location) t on t.location = table.location
GROUP BY location
ORDER BY SUM(LocationCount) DESC
SELECT location, SUM( total ) AS total, GROUP_CONCAT( CONCAT( billingno, ' - ', billing_count ) ) AS display
FROM (
SELECT location, COUNT( billingno ) AS total, billingno, COUNT( billingno ) AS billing_count
FROM billing
GROUP BY location, billingno
ORDER BY COUNT( * ) DESC
) AS t
GROUP BY location
Related
I have two main tables that comprise bookings for events.
A Registrants table (Bookings) R and an Events table E.
There are also two connected tables, Field_Values V and Event_Categories C
This diagram shows the relationship
What I am trying to do is create an Invoice query that mirrors the user's shopping cart. Often a user will book multiple events in one transaction, so my invoice should have columns for the common items e.g. User Name, User Email, Booking Date, Transaction ID and aggregated columns for the invoice line item values e.g. Quantity "1,2" Description "Desc1, Desc2" Price "10.00, 20.00" where there are two line items in the shopping cart.
The Transaction ID (dcea4_eb_registrant.transaction_id) is unique per Invoice and repeated per line item in that sale.
I have the following query which produces rows for each line item
SELECT
R.id as ID,
E.event_date as ServiceDate,
E.event_date - INTERVAL 1 DAY as DueDate,
Concat('Ad-Hoc Booking:',E.title) as ItemProductService,
Concat(R.first_name, ' ',R.last_name) as Customer,
R.first_name as FirstName,
R.last_name as LastName,
R.email,
R.register_date as InvoiceDate,
R.amount as ItemAmount,
R.comment,
R.number_registrants as ItemQuantity,
R.transaction_id as InvoiceNo,
R.published as Status,
E.event_date AS SERVICEDATE,
Concat('Ad-Hoc Booking:',E.title) AS DESCRIPTION,
R.number_registrants AS QUANTITY,
FORMAT(R.amount / R.number_registrants,2) AS RATE,
R.amount AS AMOUNT,
C.category_id as CLASS,
Concat(Group_Concat(V.field_value SEPARATOR ', '),'. ',R.comment) as Memo
FROM dcea4_eb_events E
LEFT JOIN dcea4_eb_registrants R ON R.event_id = E.id
LEFT JOIN dcea4_eb_field_values V ON V.registrant_id = R.id
LEFT JOIN dcea4_eb_event_categories C ON C.event_id = R.event_id
WHERE 1=1
AND V.field_id IN(14,26,27,15)
AND R.published <> 2 /*Including this line omits Cancelled Invoices */
AND R.published IS NOT NULL
AND (R.published = 1 OR R.payment_method = "os_offline")
AND (R.register_date >= CURDATE() - INTERVAL 14 DAY)
GROUP BY E.event_date, E.title, R.id, R.first_name, R.last_name, R.email,R.register_date, R.amount, R.comment
ORDER BY R.register_date DESC, R.transaction_id
This produces output like this
I'm using the following query to try to group together the rows with a common transaction_ID (rows two and three in the last picture) - I add group_concat on the columns I want to aggregate and change the Group By to be the transaction_id
SELECT
R.id as ID,
E.event_date as ServiceDate,
E.event_date - INTERVAL 1 DAY as DueDate,
Concat('Ad-Hoc Booking:',E.title) as ItemProductService,
Concat(R.first_name, ' ',R.last_name) as Customer,
R.first_name as FirstName,
R.last_name as LastName,
R.email,
R.register_date as InvoiceDate,
R.amount as ItemAmount,
R.comment,
R.number_registrants as ItemQuantity,
R.transaction_id as InvoiceNo,
R.published as Status,
Group_ConCat( E.event_date) AS SERVICEDATE,
Group_ConCat( Concat('Ad-Hoc Booking:',E.title)) AS DESCRIPTION,
Group_ConCat( R.number_registrants) AS QUANTITY,
Group_ConCat( FORMAT(R.amount / R.number_registrants,2)) AS RATE2,
Group_ConCat( R.amount) AS AMOUNT,
Group_ConCat( C.category_id) as CLASS,
Concat(Group_Concat(V.field_value SEPARATOR ', '),'. ',R.comment) as Memo
FROM dcea4_eb_events E
LEFT JOIN dcea4_eb_registrants R ON R.event_id = E.id
LEFT JOIN dcea4_eb_field_values V ON V.registrant_id = R.id
LEFT JOIN dcea4_eb_event_categories C ON C.event_id = R.event_id
WHERE 1=1
AND V.field_id IN(14,26,27,15)
AND R.published <> 2 /*Including this line omits Cancelled Invoices */
AND R.published IS NOT NULL
AND (R.published = 1 OR R.payment_method = "os_offline")
AND (R.register_date >= CURDATE() - INTERVAL 14 DAY)
GROUP BY R.transaction_id
ORDER BY R.register_date DESC, R.transaction_id
But this produces this output
It seems to be multiplying the rows. The Quantity column in the first row should just be 1 and in the second row it should be 2,1 .
I've tried using Group_Concat with DISTINCT but this doesn't work because often the values being concatenated are the same (e.g. the price for two events being booked are both the same) and the query only returns one value e.g. 10 and not 10, 10. The latter being what I need.
I'm guessing the issue is around the way the tables are joined but I'm struggling to work out how to get what I need.
Pointers in the right direction most appreciated.
You seem determined to go in what seems to me to be the wrong direction, so here's a gentle nudge down that hill...
Consider the following...
CREATE TABLE users
(user_id SERIAL PRIMARY KEY
,username VARCHAR(12) UNIQUE
);
INSERT INTO users VALUES
(101,'John'),(102,'Paul'),(103,'George'),(104,'Ringo');
DROP TABLE IF EXISTS sales;
CREATE TABLE sales
(sale_id SERIAL PRIMARY KEY
,purchaser_id INT NOT NULL
,item_code CHAR(1) NOT NULL
,quantity INT NOT NULL
);
INSERT INTO sales VALUES
( 1,101,'A',1),
( 2,103,'A',2),
( 3,103,'A',3),
( 4,104,'A',1),
( 5,104,'A',2),
( 6,104,'A',3),
( 7,103,'B',2),
( 8,103,'B',2),
( 9,104,'B',3),
(10,103,'B',2),
(11,104,'B',2),
(12,104,'B',1);
SELECT u.*
, x.sale_ids
, x.item_codes
, x.quantities
FROM users u
LEFT
JOIN
( SELECT purchaser_id
, GROUP_CONCAT(sale_id ORDER BY sale_id) sale_ids
, GROUP_CONCAT(item_code ORDER BY sale_id) item_codes
, GROUP_CONCAT(quantity ORDER BY sale_id) quantities
FROM sales
GROUP
BY purchaser_id
) x
ON x.purchaser_id = u.user_id;
+---------+----------+---------------+-------------+-------------+
| user_id | username | sale_ids | item_codes | quantities |
+---------+----------+---------------+-------------+-------------+
| 101 | John | 1 | A | 1 |
| 102 | Paul | NULL | NULL | NULL |
| 103 | George | 2,3,7,8,10 | A,A,B,B,B | 2,3,2,2,2 |
| 104 | Ringo | 4,5,6,9,11,12 | A,A,A,B,B,B | 1,2,3,3,2,1 |
+---------+----------+---------------+-------------+-------------+
I have my schema similar to the below
|- name
+- cars
| |- tesla integer
| |- ferrari integer
Let's say every record specifies an order for cars and every order specifies the number of different cars that were ordered.
Now I want to query the table such that I get the sum of all the different car types.
So I want something like SELECT SUM(cars.*) from table_name, because I don't know what all possible nested fields the cars might have.
The schema gets dynamically generated every time a new car model is added, and thus in my query I can't possibly specify all the field names because I won't be having them.
example data -
name | cars.tesla | cars.ferrari
vendor1 | 12 | 10
vendor1 | 5 | 5
vendor2 | 4 | 3
desired output for vendor1 -
name | total_tesla | total_ferrari
vendor1 | 17 | 15
So, I want to select sum of all the fields that are nested under the particular record. Is there a way I can do that?
Below is for BigQuery Standard SQL
#standardSQL
SELECT name, SUM(SAFE_CAST(SPLIT(kv, ':')[OFFSET(1)] AS INT64)) total
FROM `project.dataset.table` t,
UNNEST(SPLIT(REGEXP_REPLACE(JSON_EXTRACT(TO_JSON_STRING(t), '$.cars'), r'^{|}$', ''))) kv
GROUP BY name
You can test / play with above using dummy data as below
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'a' name, STRUCT<tesla INT64, ferrari INT64>(1, 2) cars UNION ALL
SELECT 'b', STRUCT(3,4)
)
SELECT name, SUM(SAFE_CAST(SPLIT(kv, ':')[OFFSET(1)] AS INT64)) total
FROM `project.dataset.table` t,
UNNEST(SPLIT(REGEXP_REPLACE(JSON_EXTRACT(TO_JSON_STRING(t), '$.cars'), r'^{|}$', ''))) kv
GROUP BY name
result is
name total
a 3
b 7
Below is simplified / refactored version of above:
#standardSQL
SELECT name,
(
SELECT SUM(SAFE_CAST(SPLIT(kv, ':')[OFFSET(1)] AS INT64))
FROM UNNEST(SPLIT(REGEXP_REPLACE(TO_JSON_STRING(cars), r'^{|}$', ''))) kv
) total
FROM `project.dataset.table`
And finally "final" version:
#standardSQL
CREATE TEMP FUNCTION SUM_NESTED(root STRING) AS (
(SELECT SUM(SAFE_CAST(SPLIT(kv, ':')[OFFSET(1)] AS INT64))
FROM UNNEST(SPLIT(REGEXP_REPLACE(root, r'^{|}$', ''))) kv)
);
SELECT name, SUM_NESTED(TO_JSON_STRING(cars)) total
FROM `project.dataset.table`
Update to address edited question
Below should give you direction - it gives you flatten result (you should search this site then for how to pivot it - there are plenty of questions/answers on that topic here)
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'vendor1' name, STRUCT<tesla INT64, ferrari INT64>(12, 10) cars UNION ALL
SELECT 'vendor1', STRUCT(5, 5) UNION ALL
SELECT 'vendor2', STRUCT(4,3) UNION ALL
SELECT 'vendor2', STRUCT(1,NULL)
)
SELECT
name,
REPLACE(SPLIT(kv, ':')[OFFSET(0)], '"', '') car,
SUM(SAFE_CAST(SPLIT(kv, ':')[OFFSET(1)] AS INT64)) total
FROM `project.dataset.table`,
UNNEST(SPLIT(REGEXP_REPLACE(TO_JSON_STRING(cars), r'^{|}$', ''))) kv
GROUP BY name, car
-- ORDER BY name, car
result is
name car total
vendor1 ferrari 15
vendor1 tesla 17
vendor2 ferrari 3
vendor2 tesla 5
I'm new to SQL and facing following problem:
This is my table:
name city people
-----|-----|--------|
John | A | 5 |
Ben | D | 6 |
John | A | 5 |
Ben | A | 5 |
John | B | 8 |
Ben | D | 6 |
I want to group by the name and receive associated to the name that city with the largest quantity. As a second query, instead of the largest quantity, that city with the highest sum of inhabitants.
This would be the outcome for the first query:
name city
-----|-----|
John | A |
Ben | D |
Thank you!
I don't know exactly what you mean by "to the name that city with the largest quantity". What I understood was you sum the column 'people' per couple (name, city), thus (John, A) would be 10 and (John, B) would be 8, and you take the max value to get (John, A).
In this case, you can do it this way:
SELECT
name,
city
FROM
(SELECT
name,
city,
SUM(people) AS tot
FROM table
GROUP BY name, city
ORDER BY name ASC, tot DESC) AS a
GROUP BY name ;
As for the city with the largest number of inhabitants, you just have to group by city and sum the column people and take the max:
SELECT
city,
SUM(people) AS nb_inhabitants
FROM table
GROUP BY city
ORDER BY nb_inhabitants DESC
LIMIT 1;
SELECT name, city, sum( people )
FROM `detail`
GROUP BY name
ORDER BY people ASC
LIMIT 0 , 30
I am not really understand what your are expecting ,but I guess you want to do this thing.
Description : I am group by people from there name , and got sum of the people and make them ASC order. I am not sure your are expect this thing.
You can also , group people by their city
SELECT name, city, sum( people )
FROM `detail`
GROUP BY city
ORDER BY people ASC
LIMIT 0 , 30
If this not you expect , Please , further describe question ,we will try to give some answer.
Try this, I have tried by making a table as per your sample data,
CREATE TABLE KaiTable
(
NAME VARCHAR(50)
,city CHAR(1)
,people INT
);
INSERT INTO KaiTable
VALUES
(
'John'
,'A'
,5
),
('Ben ' ,'D' ,6),
('John' ,'A' ,5),
('Ben ' ,'A' ,5) ,
('John' ,'B' ,8) ,
('Ben ' ,'D' ,6)
SELECT NAME,city
FROM
(SELECT NAME,city,SUM(people) AS PeopleSum
FROM KaiTable
GROUP BY NAME, city
ORDER BY NAME ASC, PeopleSum DESC) AS a
GROUP BY NAME DESC;
SQL Fiddle Demo
There are countries in MySQL table:
id | title
1 | USA
2 | Spain
3 | Italy
4 | Canada
I need to select Italy on the top of list and other countries sorted by title below.
But 'order by' doesn't work.
(SELECT * FROM countries WHERE id = 3) UNION (SELECT * FROM countries WHERE id != 3 ORDER BY title)
First sort your data based on whether it is Italy or not, getting Italy first. Then sort based on the title.
SELECT * FROM countries
ORDER BY title='Italy' DESC, title
(The only trick you have to know -- or experiment with -- is that FALSE comes before TRUE, and hence the DESC in the code. I guess that makes sense if you convert them to 0 < 1.)
you can try this
SELECT * FROM countries
ORDER BY case when id !=3 then `title` end asc ,
case when id =3 then `title` end asc ;
SELECT * FROM countries WHERE id = 3
union
SELECT c.* FROM (SELECT * FROM countries WHERE id != 3 order by title) c
I have a Table member with member_id, member_name, club_name, region, zone, email as fields.
I am using the MySQL group_concat function like
SELECT group_concat(distinct m.email
SEPARATOR ', ' ) from member m group by m.club_name
This is working fine. But I would like to be able to group_concat on other fields without creating additional queries.
Is it possible to supply the other fields as parameter?
member_id member_name club_name region zone email
1 member1 A 1 1 email1#example.com
2 member2 A 1 1 email2#example.com
3 member3 B 1 1 email3#example.com
4 member4 C 1 2 email4#example.com
5 member5 D 2 1 email5#example.com
**group by club**
email1#example.com,email2#example.com
email3#example.com
email4#example.com
email5#example.com
**group by region**
email1#example.com, email2#example.com, email3#example.com, email4#example.com
email5#example.com
**group by zone**
email1#example.com, email2#example.com, email3#example.com
email5#example.com
Say every Region has 3 Zones, every zone has more than one club. Now how can I get emails which can be grouped or related to Region, Zone or Club for that matter?
It's hard to understand what are you after exactly from your question but you can try
SELECT club_name,
GROUP_CONCAT(DISTINCT email SEPARATOR ', ' ) emails,
GROUP_CONCAT(DISTINCT member_name SEPARATOR ', ' ) members
...
FROM member
GROUP BY club_name
Sample output:
| CLUB_NAME | EMAILS | MEMBERS |
------------------------------------------------------------------------
| Club1 | m1#mail.com, m2#mail.com, m3#mail.com | Jhon, Mark, Beth |
| Club2 | m4#mail.com, m5#mail.com | Helen, Thomas |
Here is SQLFiddle demo
On a side note: providing sample data and desired output in a question like this usually improves your changes of getting your answer faster and that best fits your needs.
UPDATE: You can deeply pack information using GROUP_CONCAT() using different separators if it's what you want
SELECT 'club' group_type, GROUP_CONCAT(details SEPARATOR '|') details
FROM
(
SELECT CONCAT(club_name, ';', GROUP_CONCAT(DISTINCT email)) details
FROM member
GROUP BY club_name
) a
UNION ALL
SELECT 'region' group_type, GROUP_CONCAT(details SEPARATOR '|') details
FROM
(
SELECT CONCAT(region, ';', GROUP_CONCAT(DISTINCT email)) details
FROM member
GROUP BY region
) a
UNION ALL
SELECT 'zone' group_type, GROUP_CONCAT(details SEPARATOR '|') details
FROM
(
SELECT CONCAT(zone, ';', GROUP_CONCAT(DISTINCT email)) details
FROM member
GROUP BY zone
) a
Sample output:
| GROUP_TYPE | DETAILS |
-----------------------------------------------------------------------------------------------------------------------
| club | A;email1#example.com,email2#example.com|B;email3#example.com|C;email4#example.com|D;email5#example.com |
| region | 1;email1#example.com,email2#example.com,email3#example.com,email4#example.com|2;email5#example.com |
| zone | 1;email1#example.com,email2#example.com,email3#example.com,email5#example.com|2;email4#example.com |
Here is SQLFiddle demo
If you're using php on the client side you can then easily enough unwind details column into separate records using explode() while you're iterating over the resultset.