If I have a MySQL table looking something like this:
company_name action pagecount
-------------------------------
Company A PRINT 3
Company A PRINT 2
Company A PRINT 3
Company B EMAIL
Company B PRINT 2
Company B PRINT 2
Company B PRINT 1
Company A PRINT 3
Is it possible to run a MySQL query to get output like this:
company_name EMAIL PRINT 1 pages PRINT 2 pages PRINT 3 pages
-------------------------------------------------------------
CompanyA 0 0 1 3
CompanyB 1 1 2 0
The idea is that pagecount can vary so the output column amount should reflect that, one column for each action/pagecount pair and then number of hits per company_name. I'm not sure if this is called a pivot table but someone suggested that?
This basically is a pivot table.
A nice tutorial on how to achieve this can be found here: http://www.artfulsoftware.com/infotree/qrytip.php?id=78
I advise reading this post and adapt this solution to your needs.
Update
After the link above is currently not available any longer I feel obliged to provide some additional information for all of you searching for mysql pivot answers in here. It really had a vast amount of information, and I won't put everything from there in here (even more since I just don't want to copy their vast knowledge), but I'll give some advice on how to deal with pivot tables the sql way generally with the example from peku who asked the question in the first place.
Maybe the link comes back soon, I'll keep an eye out for it.
The spreadsheet way...
Many people just use a tool like MSExcel, OpenOffice or other spreadsheet-tools for this purpose. This is a valid solution, just copy the data over there and use the tools the GUI offer to solve this.
But... this wasn't the question, and it might even lead to some disadvantages, like how to get the data into the spreadsheet, problematic scaling and so on.
The SQL way...
Given his table looks something like this:
CREATE TABLE `test_pivot` (
`pid` bigint(20) NOT NULL AUTO_INCREMENT,
`company_name` varchar(32) DEFAULT NULL,
`action` varchar(16) DEFAULT NULL,
`pagecount` bigint(20) DEFAULT NULL,
PRIMARY KEY (`pid`)
) ENGINE=MyISAM;
Now look into his/her desired table:
company_name EMAIL PRINT 1 pages PRINT 2 pages PRINT 3 pages
-------------------------------------------------------------
CompanyA 0 0 1 3
CompanyB 1 1 2 0
The rows (EMAIL, PRINT x pages) resemble conditions. The main grouping is by company_name.
In order to set up the conditions this rather shouts for using the CASE-statement. In order to group by something, well, use ... GROUP BY.
The basic SQL providing this pivot can look something like this:
SELECT P.`company_name`,
COUNT(
CASE
WHEN P.`action`='EMAIL'
THEN 1
ELSE NULL
END
) AS 'EMAIL',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '1'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 1 pages',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '2'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 2 pages',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '3'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 3 pages'
FROM test_pivot P
GROUP BY P.`company_name`;
This should provide the desired result very fast. The major downside for this approach, the more rows you want in your pivot table, the more conditions you need to define in your SQL statement.
This can be dealt with, too, therefore people tend to use prepared statements, routines, counters and such.
Some additional links about this topic:
http://anothermysqldba.blogspot.de/2013/06/pivot-tables-example-in-mysql.html
http://www.codeproject.com/Articles/363339/Cross-Tabulation-Pivot-Tables-with-MySQL
http://datacharmer.org/downloads/pivot_tables_mysql_5.pdf
https://codingsight.com/pivot-tables-in-mysql/
My solution is in T-SQL without any pivots:
SELECT
CompanyName,
SUM(CASE WHEN (action='EMAIL') THEN 1 ELSE 0 END) AS Email,
SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END) AS Print1Pages,
SUM(CASE WHEN (action='PRINT' AND pagecount=2) THEN 1 ELSE 0 END) AS Print2Pages,
SUM(CASE WHEN (action='PRINT' AND pagecount=3) THEN 1 ELSE 0 END) AS Print3Pages
FROM
Company
GROUP BY
CompanyName
For MySQL you can directly put conditions in SUM() function and it will be evaluated as Boolean 0 or 1 and thus you can have your count based on your criteria without using IF/CASE statements
SELECT
company_name,
SUM(action = 'EMAIL')AS Email,
SUM(action = 'PRINT' AND pagecount = 1)AS Print1Pages,
SUM(action = 'PRINT' AND pagecount = 2)AS Print2Pages,
SUM(action = 'PRINT' AND pagecount = 3)AS Print3Pages
FROM t
GROUP BY company_name
DEMO
For dynamic pivot, use GROUP_CONCAT with CONCAT.
The GROUP_CONCAT function concatenates strings from a group into one string with various options.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN action = "',
action,'" AND ',
(CASE WHEN pagecount IS NOT NULL
THEN CONCAT("pagecount = ",pagecount)
ELSE pagecount IS NULL END),
' THEN 1 ELSE 0 end) AS ',
action, IFNULL(pagecount,'')
)
)
INTO #sql
FROM
t;
SET #sql = CONCAT('SELECT company_name, ', #sql, '
FROM t
GROUP BY company_name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DEMO HERE
A stardard-SQL version using boolean logic:
SELECT company_name
, COUNT(action = 'EMAIL' OR NULL) AS "Email"
, COUNT(action = 'PRINT' AND pagecount = 1 OR NULL) AS "Print 1 pages"
, COUNT(action = 'PRINT' AND pagecount = 2 OR NULL) AS "Print 2 pages"
, COUNT(action = 'PRINT' AND pagecount = 3 OR NULL) AS "Print 3 pages"
FROM tbl
GROUP BY company_name;
db<>fiddle here
Old sqlfiddle
How?
TRUE OR NULL yields TRUE.
FALSE OR NULL yields NULL.
NULL OR NULL yields NULL.
And COUNT only counts non-null values. Voilá.
Correct answer is:
select table_record_id,
group_concat(if(value_name='note', value_text, NULL)) as note
,group_concat(if(value_name='hire_date', value_text, NULL)) as hire_date
,group_concat(if(value_name='termination_date', value_text, NULL)) as termination_date
,group_concat(if(value_name='department', value_text, NULL)) as department
,group_concat(if(value_name='reporting_to', value_text, NULL)) as reporting_to
,group_concat(if(value_name='shift_start_time', value_text, NULL)) as shift_start_time
,group_concat(if(value_name='shift_end_time', value_text, NULL)) as shift_end_time
from other_value
where table_name = 'employee'
and is_active = 'y'
and is_deleted = 'n'
GROUP BY table_record_id
There is a tool called MySQL Pivot table generator, it can help you create a web-based pivot table that you can later export to excel(if you like). it can work if your data is in a single table or in several tables.
All you need to do is to specify the data source of the columns (it supports dynamic columns), rows, the values in the body of the table, and table relationship (if there are any)
The home page of this tool is https://mysqlreports.com/mysql-reporting-tools/mysql-pivot-table/
select t3.name, sum(t3.prod_A) as Prod_A, sum(t3.prod_B) as Prod_B, sum(t3.prod_C) as Prod_C, sum(t3.prod_D) as Prod_D, sum(t3.prod_E) as Prod_E
from
(select t2.name as name,
case when t2.prodid = 1 then t2.counts
else 0 end prod_A,
case when t2.prodid = 2 then t2.counts
else 0 end prod_B,
case when t2.prodid = 3 then t2.counts
else 0 end prod_C,
case when t2.prodid = 4 then t2.counts
else 0 end prod_D,
case when t2.prodid = "5" then t2.counts
else 0 end prod_E
from
(SELECT partners.name as name, sales.products_id as prodid, count(products.name) as counts
FROM test.sales left outer join test.partners on sales.partners_id = partners.id
left outer join test.products on sales.products_id = products.id
where sales.partners_id = partners.id and sales.products_id = products.id group by partners.name, prodid) t2) t3
group by t3.name ;
One option would be combining use of CASE..WHEN statement is redundant within an aggregation for MySQL Database, and considering the needed query generation dynamically along with getting proper column title for the result set as in the following code block :
SET #sql = NULL;
SELECT GROUP_CONCAT(
CONCAT('SUM( `action` = ''', action, '''',pc0,' ) AS ',action,pc1)
)
INTO #sql
FROM
(
SELECT DISTINCT `action`,
IF(`pagecount` IS NULL,'',CONCAT('page',`pagecount`)) AS pc1,
IF(`pagecount` IS NULL,'',CONCAT(' AND `pagecount` = ', pagecount, '')) AS pc0
FROM `tab`
ORDER BY CONCAT(action,pc0)
) t;
SET #sql = CONCAT('SELECT company_name,',#sql,' FROM `tab` GROUP BY company_name');
SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Demo
SELECT company_name, SUM(CASE WHEN ACTION = 'Email' THEN 1 ELSE 0 END) AS "Email",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 1 THEN 1 ELSE 0 END) AS "print 1 PAGE",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 2 THEN 1 ELSE 0 END) AS "print 2 PAGE",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 3 THEN 1 ELSE 0 END) AS "print 2 PAGE"
FROM test1 GROUP BY company_name;
I have two temp tables (below), the first marks one of five conditions. The second pulls from that table and does a sum and a count based on the condition. How could I get the second table to work with this, or a similar format?
select ID
,sum_value
,condition_field
,'condition_1' = case when condition_type in (1,2) then 1 else 0 end
,'condition_2' = case when condition_type in (3,4) then 1 else 0 end
--etc...
into #temp
from my_table
select ID
,sum_value
,'1_amt' = SUM(sum_value) from #temp where condition_1 = 1
,'1_cnt' = COUNT(ID) from #temp where condition_1 = 1
,'2_amt' = SUM(sum_value) from #temp where condition_2 = 1
,'2_cnt' = COUNT(ID) form #temp where condition_2 = 2
from #temp
You want something more like this:
SUM(CASE WHEN condition_1=1 THEN sum_value ELSE 0 END) AS 1_amt
I am doing a exercise
I'll fire a query on the DB and get some 500 results. Now i want to sort this list based on some conditions and present the sorted list in client side.
I am using Java/Java EE and MySQL server 5.5
Conditions are like this,
Example: Consider a table having listed with cars
So, i ll fire a query on the table and it will list some 500 cars. now i want to sort this list based on user criteria.
conditions are age of car, colour of car and facilities of cars. List should be sorted like this
First appears the list of cars which satisfies all three conditions ie., same age as mentioned by end user, same colour and with all facilities user selected.
Second appears any 2 conditions satisfying cars list and one condition not satifying.
Third appears any one condition satisfying cars list and not the other two.
And finally appears the list of cars of which no conditions are satisfied.
How can i achieve this. I have searched in google, asked in irc channels regarding this. Couldn't get any help.
I have tried using RANK function by defining the CASES and finally order by RANK. It works for me while the conditions fields (columns) are of same table. In my case the fields are from a parent table as well as its child tables which has many to one relationship with its parent. Like in this example, age and color of the cars are stored in parent table and facilities that cars has are stored in another table. I tried doing the same using inner join, but no luck.
I tried something like this:
Query:
select distinct t0.id,t0.name,t0.price,
CASE
WHEN
t1.age='2' AND t1.colour='Red' AND t2.facilities_id=9 THEN 1
WHEN
t1.age='2' AND t1.colour='Red' AND t2.facilities_id!=9 THEN 2
WHEN
t1.age='2' AND t1.colour!='Red' AND t2.facilities_id=9 THEN 3
WHEN
t1.age!='2' AND t1.colour='Red' AND t2.facilities_id=9 THEN 4
WHEN
t1.age!='2' AND t1.colour='Red' AND t2.facilities_id!=9 THEN 5
WHEN
t1.age='2' AND t1.colour!='Red' AND t2.facilities_id!=9 THEN 6
WHEN *
t1.age!='2' AND t1.colour!='Red' AND t2.facilities_id=9 THEN 7
ELSE 8
END as pre_status
from cars_listing t0
inner join
cars_listing_details t1
on t0.id=t1.mg_listing_id
inner join
cars_facilities_listing t2
on t1.cars_listing_id=t2.listing_id
where t0.type='new_cars'
order by pre_status
Thanks in advance for helping.
try ordering by something like...
order by
case when first_condition then 1 else 0 end
+ case when second_condition then 1 else 0 end
+ case when third_condition then 1 else 0 end DESC
select distinct
t0.id,
t0.name,
t0.price,
case when t1.age = '2' then 1 else 0 end as MatchedAge,
case when t1.colour='Red' then 1 else 0 end as MatchedColor,
case when t2.facilities_id = 9 THEN 1 else 0 end as MatchedFacility
from
cars_listing t0
inner join cars_listing_details t1
on t0.id = t1.mg_listing_id
inner join cars_facilities_listing t2
on t1.cars_listing_id = t2.listing_id
where
t0.type = 'new_cars'
order by
case when t1.age = '2' then 1 else 0 end
+ case when t1.colour='Red' then 1 else 0 end
+ case when t2.facilities_id = 9 THEN 1 else 0 end DESC
If one field is a higher priority -- such as a red car, you could even give that more weight than the other in the order by... So a Red car at Facility 5 would show before a Blue car at facility 9 just by changing the order by to something like
order by
case when t1.age = '2' then 1 else 0 end
+ case when t1.colour='Red' then 5 else 0 end <-- applyi higher Wgt to color match vs other criteria
+ case when t2.facilities_id = 9 THEN 1 else 0 end DESC
Well, I have done Dynamic sql where condition in my project. It might help you. I have created a stored procedure for SELECT query. (I have done it in SQL Server 2008 R2). Tell me if you need more help.
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[PROCEDURE_NAME]
#Id int = NULL,
#Requester varchar(20) = NULL,
#Suggester varchar(20) = NULL
AS
BEGIN
DECLARE #sql nvarchar(4000)
SELECT #sql='SELECT Id, Suggester, Requester from DATABASE_NAME.dbo.TABLE_NAME WHERE 1=1 '
If (#Id) IS NOT NULL
SELECT #sql=#sql + ' AND Id=(#Id) '
If (#Suggester) IS NOT NULL
SELECT #sql=#sql + ' AND Suggester like (#Suggester) '
If (#Requester) IS NOT NULL
SELECT #sql=#sql + ' AND Requester like (#Requester) '
EXEC sp_executesql #sql, N'#id int, #Requester varchar(20), #Suggester varchar(20)',
#Id, #Requester, #Suggester
END
GO
Here in this SP; Id,Requester,Suggester are field names.
Basically, what syntex would allow me to achieve the title statement?
If (select statement 1) returns 0 rows THEN (select statement 2) else (select statement 3)
So that the sql returns results from either statement 2 or 3
I've looked for a way to do this but nothing I've found so far seems to exactly address the if requirements.
IF EXISTS (SELECT field FROM table)
BEGIN
SELECT field FROM table2
END
ELSE
BEGIN
SELECT field FROM table3
END
Here you go...
IF ((select count(*) from table1)= 0)
BEGIN
Select * from table2
END
ELSE
BEGIN
SELECT * from table3
END
Sorry for the lack of feedback. Someone else in the office took an interest and came up with this:
select * from (
select *
, (SELECT Count(*)
FROM users
WHERE version_replace = 59 AND moderated = 1) AS Counter
FROM users WHERE version_replace = 59 AND moderated in (0,1)
) AS y
where Counter = 0 and Moderated = 0
or Counter > 0 and Moderated = 1
ORDER By ID DESC
Which does what I need.
I'm trying to create a query that would generate a cross-check table with about 40 custom columns that show Y or N. Right now I have
SELECT DISTINCT [Company],
[Option1],
[Option2],
[Option3],
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 1 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 2 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 3 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
.............
-- REPEAT ANOTHER 40 times
FROM [Table1]
WHERE [Table1].[OtherCondition] = 'True'
ORDER BY [Company]
So my question is, how do I create a loop (while? for?) that will loop on variable and assign Y or N to the row based on the condition, rather than creating 40+ Case statements?
You couldn't use a loop, but you could create a stored procedure/function to perform the sub-select and case expression and call that 40 times.
Also, you could improve performance of the sub-select by changing it to
SELECT 1 FROM Table2 WHERE EXISTS [Table2].[ID2] = [Table1.ID1] AND Variable = 3 AND Bit = 1
A loop (that is, iterating through a cursor) works on rows, not columns. You will still have to have 40 expressions, one for each column, and the performance will be terrible.
Let SQL Server do its job. And do your bit by telling exactly what you need and creating proper indices. That is, replace
CASE WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 2 AND Bit = 1)
with
CASE WHEN EXISTS (SELECT 0 FROM Table2 WHERE ID2 = [Table1].[ID1] AND Variable = 2 AND Bit = 1)
If the output is so vastly different than the schema, there is a question as to whether the schema properly models the business requirements. That said, I would recommend just writing the SQL. You can simplify the SQL like so:
Select Company
, Option1, Option2, Option3
, Case When T2.Variable = 1 Then 'Y' Else 'N' End As CustomCol1
, Case When T2.Variable = 2 Then 'Y' Else 'N' End As CustomCol2
, Case When T2.Variable = 3 Then 'Y' Else 'N' End As CustomCol3
, Case When T2.Variable = 4 Then 'Y' Else 'N' End As CustomCol4
...
From Table1 As T1
Left Join Table2 As T2
On T2.ID2 = T1.ID
And T2.Bit = 1
Where T1.OtherCondition = 'True'
Group By T1.Company
Order By T1.Company
If you want to write something that can help you auto-gen those Case statements (and you are using SQL Server 2005+), you could do something like:
With Numbers As
(
Select 0 As Value
Union All
Select Value + 1
From Numbers
Where Value < 41
)
Select ', Case When T2.Variable = ' + Cast(N.Value As varchar(10)) + ' Then ''Y'' Else ''N'' End As CustomCol' + Cast(N.Value As varchar(10))
From Numbers As N
You would run the query and copy and paste the results into your procedure or code.
One way could have been to use Pivot statement, which is in MS SQL 2005+. But even in that you have to put 1 ... 40 hardcoded columns in pivot statement.
Other way i can think of is to create dynamic SQL, but it is not so much recommended, So what we can do is we can create a dynamic sql query by running a while loop on table and can create the big sql and then we can execute it by using sp_execute. So steps would be.
int #loopVar
SET #loopVar = 0
int #rowCount
varchar #SQL
SET #SQl = ''
Select #rowcount = Count(ID2) from Table2
WHILE(#loopVar <= #rowCount)
BEGIN
// create ur SQL here
END
sp_execute(#SQL)