I have a MySQL table where I'm trying to find a person by his full name. The problem is that first and last name are stored on two separate rows in the table as shown here:
+-----------------+-------------+-------------+--------------+
| submit_time | form_name | field_name | field_value |
+-----------------+-------------+-------------+--------------+
| 1323463601.0947 | Online Form | firstname | Paulo |
| 1323463601.0947 | Online Form | lastname | Hill |
+-----------------+-------------+-------------+--------------+
How can I construct a query that will get a single result (if possible) by searching for Paulo Hill?
Also, the submit_time column should have the same value for both rows--that will be the one column value that is unique in the table.
You could use a self-join:
SELECT t1.submit_time, t1.field_value, t2.field_value
FROM your_table t1
INNER JOIN your_table t2 ON t2.submit_time = t1.submit_time
WHERE t1.field_name = 'firstname'
AND t1.field_value = 'Paulo'
AND t2.field_name = 'lastname'
AND t2.field_value = 'Hill'
Supposing 'paulo hill' is a single search query:
SELECT t1.*,
t2.field_value
FROM tbl t1
INNER JOIN tbl t2 ON t1.submit_time = t2.submit_time
AND t2.field_name = 'lastname'
WHERE t1.field_name = 'firstname'
AND t1.field_value || ' ' || t2.field_value = 'paulo hill'
Note: this solution is sensitive to amount of spaces between first name and last name in search query
try this:
SELECT * FROM (
SELECT submit_time, group_concat(field_value SEPARATOR ' ') AS fullname
FROM TABLE_NAME
GROUP BY `submit_time`
) AS innertable
WHERE fullname='Paulo Hill'
One alternative is to use GROUP_CONCAT if you know the order:
SELECT GROUP_CONCAT(field_value SEPARATOR ' ')
FROM tab
GROUP BY submit_time
HAVING GROUP_CONCAT(field_value ORDER BY field_id ASC) LIKE '%Paulo,Hill%'
Related
I need to put a record at last of the result set ordered by a column field in ascending order.
LIKE:
SELECT "#TOTAL"...
Which is a user-defined column value.
Here's what I am trying to do:
SELECT cnt.name as Client, NULL, NULL, NULL, ', #COL_SUM, '
FROM
task as tsk
LEFT JOIN client cnt
ON tsk.client_id = cnt.id
GROUP BY tsk.client_id
UNION ALL
SELECT "#TOTAL#",NULL,NULL,NULL, ', #COL_SUM, '
FROM task as tsk
ORDER BY Client ASC
This returns result set as:
Client | ... | Admin | Intern | ..
---------------------------------------
#TOTAL# | ... | 4 | 2 | .. <-- this row here is grand total
A | ... | 1 | | ..
B | ... | 1 | 1 | ..
C | ... | 2 | 1 | ..
I want the #TOTAL# row to be at last.
What character, except z, comes at last in alphabetical order?
Just add your condition to the ORDER BY clause:
order by Client = '#TOTAL#', Client
You could add another constant column to the individual queries that indicate if it's a total or a regular row and order by that first.
SELECT *
FROM (SELECT 0 first_order,
cnt.name client,
...
UNION ALL
SELECT 1 first_order,
'#TOTAL#' client,
...) x
ORDER BY first_order,
client;
Try this variant of code.
For mysql:
SELECT 0 as [OrderBy], cnt.name as Client, NULL, NULL, NULL, ', #COL_SUM, '
FROM
task as tsk
LEFT JOIN client cnt
ON tsk.client_id = cnt.id
GROUP BY tsk.client_id
UNION ALL
SELECT 1 , "#TOTAL#",NULL,NULL,NULL, ', #COL_SUM, '
FROM task as tsk
ORDER BY [ORDERBY], cLIENT_ID
For sql server
SELECT * FROM (
SELECT TOP 100 PERCENT cnt.name as Client, NULL, NULL, NULL, ', #COL_SUM, '
FROM
task as tsk
LEFT JOIN client cnt
ON tsk.client_id = cnt.id
GROUP BY tsk.client_id
ORDER BY TSK.CLIENT_ID) AS DATA
UNION ALL
SELECT "#TOTAL#",NULL,NULL,NULL, ', #COL_SUM, '
FROM task as tsk
Since you are not able to apply order by in the first part of your query, so there is a method to do so, top 100 percent, and you can apply order by clause in your inner query or sub query.
Hope this helps.
Table: statistics
id | user | Message
----------------------
1 | user1 |message1
2 | user2 |message2
3 | user1 |message3
I am able to find the count of messages sent by each user using this query.
select user, count(*) from statistics group by user;
How to show message column data along with the count? For example
user | count | message
------------------------
user1| 2 |message1
|message3
user2| 1 |message2
You seem to want to show Count by user, which message sent by user.
If your mysql version didn't support window functions, you can do subquery to make row_number in select subquery, then only display rn=1 users and count
CREATE TABLE T(
id INT,
user VARCHAR(50),
Message VARCHAR(100)
);
INSERT INTO T VALUES(1,'user1' ,'message1');
INSERT INTO T VALUES(2,'user2' ,'message2');
INSERT INTO T VALUES(3,'user1' ,'message3');
Query 1:
SELECT (case when rn = 1 then user else '' end) 'users',
(case when rn = 1 then cnt else '' end) 'count',
message
FROM (
select
t1.user,
t2.cnt,
t1.message,
(SELECT COUNT(*) from t tt WHERE tt.user = t1.user and t1.id >= tt.id) rn
from T t1
join (
select user, count(*) cnt
from T
group by user
) t2 on t1.user = t2.user
) t1
order by user,message
Results:
| users | count | message |
|-------|-------|----------|
| user1 | 2 | message1 |
| | | message3 |
| user2 | 1 | message2 |
select user, count(*) as 'total' , group_concat(message) from statistics group by user;
You could join the result of your group by with the full table (or vice versa)?
Or, depending on what you want, you could use group_concat() using \n as separator.
Use Group_concat
select user, count(0) as ct,group_concat(Message) from statistics group by user;
This will give you message in csv format
NOTE: GROUP_CONCAT has size limit of 1024 characters by default in mysql.
For UTF it goes to 1024/3 and utfmb4 255(1024/4).
You can use group_concat_max_len global variable to set its max length as per need but take into account memory considerations on production environment
SET group_concat_max_len=100000000
Update:
You can use any separator in group_concat
Group_concat(Message SEPARATOR '----')
Try grouping with self-join:
select s1.user, s2.cnt, s1.message
from statistics s1
join (
select user, count(*) cnt
from statistics
group by user
) s2 on s1.user = s2.user
I am creating an Angularjs app with two tables "Contact Group" and "Contact List". The contact list table items have corespondent id to items in the contact group.
What I want to achieve is a MySQL/Sqlite select query that will have each contact list items as a child object of its parent.
contact_group
id | title
------------
1 | friends
2 | mates
3 | family
contact_list
id | gID | name | number
--------------------------
1 | 1 | dave |0208500
2 | 1 | dan |4213839
3 | 1 | sans |5656434
4 | 2 | fola |1918982
5 | 3 | brian|2398343
6 | 5 | grace|0934232
Select query results examples (this is what i want to get)
id: 1
title: friends
child: {id:1, name:dave, number:0208500}, {id:2, name:dan, number:4213839},{id:3, name:sans, number:5656434}
I case there is another way of doing it, this what i want to achieve. I have created an accordion with title from the contact_group title. Under each accordion are the correspondent contacts from the contact_list.
I will be glad if anyone can help me. Thank you
In order to achieve this you will have to use MySql GROUP_CONCAT (documentation here: https://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat)
Using GROUP_CONCAT we will be able to join all matching rows from the contacts TB into a string in one column. With some more formatting of the text we glue together from the contacts TB and additional usage of CONCAT we will build the string in this column as a JSON string that you will then be able to work with on your application.
Here is the SQL:
SELECT
a.id,
a.title,
CONCAT(
'[',
GROUP_CONCAT(
CONCAT(
'{"id":',
b.id,
', "name":"',
b. `name`,
'", "number":"',
b.number,
'"}'
)
ORDER BY
b.`name`
), ']')
AS people
FROM
contact_group AS a
LEFT JOIN contact_list AS b ON a.id = b.gID
GROUP BY
a.id,
a.title
And this is the result we will get:
Don't forget to add Indexes and Foreign keys to your tables so query processing would be better.
SQLLITE Version:
in order to adjust the query to sqllite, there are 2 features that need to be changed.
SqlLite uses || operator instead of CONCAT and it does not support ORDER BY inside the GROUP_CONCAT. so for the SQLLITE DB the query will look like this:
SELECT
a.id,
a.title,
'[' ||
GROUP_CONCAT(
'{"id":' ||
b.id ||
', "name":"' ||
b. `name` ||
'", "number":"' ||
b.number ||
'"}'
) || ']'
AS people
FROM
contact_group AS a
LEFT JOIN contact_list AS b ON a.id = b.gID
GROUP BY
a.id,
a.title
MySQL Fiddle Demo
SqlLite Fiddle Demo
Try the below query,
select * from contact_group join contact_list on contact_group.id= contact_list.gid
My issue is that I have a table apidata that holds multiple rows of data for each domain. So when I query apidata I naturally get multiple rows as a result. Is there any way to turn those rows into columns? I ask because I'm already using a query to pull the domain data (page title, URL, top level domain, ip address etc) and I need to add the api data with it. I believe I have to do this in two queries but I would love to at least have one row per domain to make the query and loop as fast a possible.
So the question is, can I create columns out of rows on the fly?
Heres a SQL Fiddle => http://sqlfiddle.com/#!2/8e408/4
(Note, I didnt put the whole database in the fiddle just the tables that effect the query. If you think somethings missing that you need, let me know.)
Tool_Runs (id_sha is the main lookup value for tool runs)
| ID | ID_SHA |
+----+------------------------------------------+
| 1 | 68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A |
Domains (Run_id is FK to tool_runs.id)
| ID | RUN_ID |
+----+--------+
| 1 | 1 |
API Data
| ID | DOMAIN_ID | EXPORT_COLUMN | COLUMN_TITLE | VALUE |
+----+-----------+------------------+-------------------+-------+
| 1 | 1 | referringDomains | Referring Domains | 10 |
+----+-----------+------------------+-------------------+-------+
| 2 | 1 | linkCount | Backlink Count | 55 |
Heres my query now:
SELECT a.domain_id, a.export_column, a.column_title, a.value
FROM apidata AS a WHERE domain_id IN
(
SELECT d.id FROM tool_runs AS t
JOIN domains AS d ON d.run_id = t.id
WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
)
ORDER BY a.domain_id
And what I get is:
| DOMAIN_ID | EXPORT_COLUMN | COLUMN_TITLE | VALUE |
+-----------+------------------+-------------------+----------+
| 1 | referringDomains | Referring Domains | 10 |
+-----------+------------------+-------------------+----------+
| 1 | linkCount | Backlink Count | 55 |
But what I want is
| DOMAIN_ID | referringDomains | referringDomains_TITLE | linkCount | linkCount_TITLE |
+-----------+------------------+------------------------+-----------+-----------------+
| 1 | 10 | Referring Domains | 55 | Backlink Count |
What you are trying to is to pivot the table rows into columns. Unfortunately MySQL doesn't have a native pivot table operator, but you can use the CASE expression to do so:
SELECT
a.Domain_id,
MAX(CASE WHEN a.export_column = 'referringDomains' THEN a.value END) AS referringDomains,
MAX(CASE WHEN a.export_column = 'referringDomains' THEN a.column_title END) AS referringDomains_TITLE,
MAX(CASE WHEN a.export_column = 'linkCount' THEN a.value END) AS linkCount,
MAX(CASE WHEN a.export_column = 'linkCount' THEN a.column_title END) AS linkCount_TITLE
FROM apidata AS a
WHERE domain_id IN
(
SELECT d.id FROM tool_runs AS t
JOIN domains AS d ON d.run_id = t.id
WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
)
GROUP BY a.domain_id;
Updated SQL Fiddle Demo
Note that: If you want to do so for all the values in the export_column, you have to write a CASE expression for each value. But you can do that using dynamic sql like this:
SET #ecvalues = NULL;
SET #ectitles = NULL;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.export_column = ''',
a.export_column, ''', a.value , NULL)) AS ', '''', a.export_column , '''')
) INTO #ecvalues
FROM apidata a;
SELECT
GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.export_column = ''',
a.export_column, ''', column_title , NULL)) AS ', '''', CONCAT(a.export_column , '_Titles'), '''')
) INTO #ectitles
FROM apidata a;
SET #sql = CONCAT('SELECT
a.Domain_id, ', #ectitles , ',', #ecvalues, '
FROM apidata AS a
WHERE domain_id IN
(
SELECT d.id FROM tool_runs AS t
JOIN domains AS d ON d.run_id = t.id
WHERE id_sha = ''68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A''
)
GROUP BY a.domain_id;');
prepare stmt
FROM #sql;
execute stmt;
You can put that query inside a stored procedure.
Updated SQL Fiddle Demo
Just as a complement to the #MahmoudGamal answer you should know that for any new registry (EXPORT_COLUMN) you will have to add a new case statement.
So in order to do it dynamic you can create a procedure as described on this post at dba.stackexchange.
How to transpose/convert rows as columns in mysql
It shows how to do it dynamically.
If you want columns, go ahead and pivot as the example above. If you only want a single string, for some reporting reason, go ahead and do:
SELECT group_concat(CONCAT_WS(' ',a.domain_id, a.value, a.column_title, a.export_column, 'next row string separator'))
FROM apidata AS a WHERE domain_id IN
(
SELECT d.id FROM tool_runs AS t
JOIN domains AS d ON d.run_id = t.id
WHERE id_sha = '68300DF58B2A8A6E098CB0B3D1A9AE80BBE5897A'
)
ORDER BY a.domain_id
Please consider the following table structure and data:
+--------------------+-------------+
| venue_name | listed_by |
+--------------------+-------------+
| My Venue Name | 1 |
| Another Venue | 2 |
| My Venue Name | 5 |
+--------------------+-------------+
I am currently using MySQL's GROUP BY function to select only unique venue names. However, this only returns the first occurance of My Venue Name, but I would like to return it based on a condition (in this case where the listed_by field has a value > 2.
Essentially here's some pseudo-code of what I'd like to achieve:
Select all records
Group by name
if grouped, return the occurance with the higher value in listed_by
Is there an SQL statement that will allow this functionality?
Edit: I should have mentioned that there are other fields involved in the query, and the listed_by field needs to be used elsewhere in the query, too. Here is the original query that we're using:
SELECT l1.field_value AS venue_name,
base.ID AS listing_id,
base.user_ID AS user_id,
IF(base.user_ID > 1, 'b', 'a') AS flag,
COUNT(img.ID) AS img_num
FROM ( listingsDBElements l1, listingsDB base )
LEFT JOIN listingsImages img ON (base.ID = img.listing_id AND base.user_ID = img.user_id and img.active = 'yes')
WHERE l1.field_name = 'venue_name'
AND l1.field_value LIKE '%name%'
AND base.ID = l1.listing_id
AND base.user_ID = l1.user_id
AND base.ID = l1.listing_id
AND base.user_ID = l1.user_id
AND base.active = 'yes'
GROUP BY base.Title ORDER BY flag desc,img_num desc
As long as you didn't mention other fields - here is the simplest solution:
SELECT venue_name,
MAX(listed_by)
FROM tblname
WHERE listed_by > 2
GROUP BY venue_name
With other fields it could look like (assuming there is no duplicates in venue_name + listed_by pairs):
SELECT *
FROM tblname t1
INNER JOIN (SELECT venue_name,
MAX(listed_by) max_listed_by
FROM tblname
WHERE listed_by > 2
GROUP BY venue_name) t2 ON t1.venue_name = t2.venue_name
AND t1.listed_by = t2.max_listed_by