turn the result table mysql - mysql

I have a schema in Mysql database:
CREATE TABLE test
(
ID int,
Country varchar(50),
category varchar(10)
);
INSERT INTO test VALUES (1,'USA','A');
INSERT INTO test VALUES (2,'USA','A');
INSERT INTO test VALUES (3,'USA','B');
INSERT INTO test VALUES (4,'Canada','A');
with this query :
SELECT country,count(category),category FROM test GROUP BY country,category;
I get this result :
+---------+-------+----------+
| Country | count | category |
+---------+-------+----------+
| Canada | 1 | A |
| USA | 2 | A |
| USA | 1 | B |
+---------+-------+----------+
but I want get like this result :
+---------+---+---+
| Country | A | B |
+---------+---+---+
| Canada | 1 | 0 |
| USA | 2 | 1 |
+---------+---+---+
Any advice will be nice. Thanks
here is my SQL Fiddle

SELECT country,
sum(case when category = 'A' then 1 else 0 end) as A,
sum(case when category = 'B' then 1 else 0 end) as B
FROM test
GROUP BY country;
SQLFiddle demo

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

Select sum of column according to case statement in mysql query

I have a table with three columns:
1. store name
2. data type (sales, return)
3. qty
---------------------------
| Stores | Data | Qty |
---------------------------
| HM | Sales | 15 |
| RD | Sales | 10 |
| HM | Return | 4 |
| RD | Return | 2 |
I want to select all store names, sales qty, return qty as following
--------------------------
| Store | Sales | Return |
--------------------------
| HM | 15 | 4 |
| RD | 10 | 2 |
Here's what I've tried:
SELECT store,
CASE `data`
WHEN 'Sales' THEN SUM(qty)
ELSE NULL
END as `Sales`,
CASE `data`
WHEN 'Return' THEN SUM(qty)
ELSE NULL
END as `Return`
FROM `full_report`
GROUP BY store
Result: I get wrong sales qty and Null for return qty!
You can use conditional aggregation . . . mixing case with sum():
select fr.store,
SUM(case when fr.data = 'Sales' then fr.qty else 0 end) as Sales,
SUM(case when fr.data = 'Return' then fr.qty else 0 end) as Returns
from full_report fr
group by fr.store;

Exctract specific data with self-check

I have this kind of table with data:
+-------------+------------+------------+
| Name | Test1 | Test2 |
+-------------+------------+------------+
| A | 1 | 1 |
+-------------+------------+------------+
| A | 1 | 1 |
+-------------+------------+------------+
| A | 0 | 2 |
+-------------+------------+------------+
| B | 1 | 1 |
+-------------+------------+------------+
| B | 2 | 1 |
+-------------+------------+------------+
| C | 1 | 1 |
+-------------+------------+------------+
| C | 1 | 1 |
+-------------+------------+------------+
I need to get all names that have all in field test1 and test2 values 1.
In this case output should be like:
+-------------+------------+
| C | PASS |
+-------------+------------+
Because all records whose name is C and have Test1=1 and Test2=1 are passed,
For example record A cannot pass because one of row have Test1=0 and Test=2.
Same is for B. B have only one record with Test1=1 and Test2=1 but the next record for B have Test1=2 and Test2=1.
How to make query that can extract those data? Or this is better to solve through code?
Combine both Test1 and Test2 columns with UNION.
Then select the name which having both minimum and maximum test value as 1.
Query
select name,'PASS' as `status`
from
(
select name,test1 as test
from tests
union all
select name,test2 as test
from tests
)t
group by name
having max(t.test) = 1
and min(t.test) = 1;
SQL Fiddle
My idea is to first choose names that do not fill your rule and then select all remaining ones. Is this your logic?
select distinct name, 'PASS' from table
where name not in
(select name from table where test1 <> 1 or test2 <> 1);
SELECT DISTINCT name, "PASS" FROM yourtable WHERE test1 = 1 AND test2=1

how to create a counting column for a particular item

I have the following schema (mysql)
create table test(
userid int(11) not null,
item varchar(15),
bookid int(11));
insert into test values ('1','journal',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('1','book',NULL);
insert into test values ('2','book',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('1','book',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('3','book',NULL);
insert into test values ('1','book',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('3','journal',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('2','book',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('3','book',NULL);
insert into test values ('3','book',NULL);
insert into test values ('3','book',NULL);
insert into test values ('3','book',NULL);
whenever there is a book, I'm trying assign an auto increment beginning with 1 in the bookid column. For each user, the numbering begins again from 1. I know a way this can be done by creating a separate table. Is there a way I can avoid that and accomplish that using some sort of update query in this very table and update the column bookid? I am trying to get output similar to the following:
userid,item,bookid
'1','journal',NULL
'1','journal',NULL
'1','book',1
'2','book',1
'2','journal',NULL
'1','book',2
'2','journal',NULL
'3','book',1
'1','book',3
'1','journal',NULL
'3','journal',NULL
'1','journal',NULL
'2','journal',NULL
'2','book',2
'2','journal',NULL
'1','journal',NULL
'3','book',2
'3','book',3
'3','book',4
'3','book',5
I appreciate if someone could guide me on how to accomplish this?
Here's one idea...
drop table if exists test;
create table test
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,userid int not null
,item varchar(15) NOT NULL
);
insert into test (userid,item) values
(1,'journal')
,(1,'journal')
,(1,'book')
,(2,'book')
,(2,'journal')
,(1,'book')
,(2,'journal')
,(3,'book')
,(1,'book')
,(1,'journal')
,(3,'journal')
,(1,'journal')
,(2,'journal')
,(2,'book')
,(2,'journal')
,(1,'journal')
,(3,'book')
,(3,'book')
,(3,'book')
,(3,'book');
SELECT x.*
, COUNT(*) rank
FROM test x
JOIN test y
ON y.userid = x.userid
AND y.item = x.item
AND y.id <= x.id
GROUP
BY id
ORDER
BY userid
, item
, rank;
+----+--------+---------+------+
| id | userid | item | rank |
+----+--------+---------+------+
| 3 | 1 | book | 1 |
| 6 | 1 | book | 2 |
| 9 | 1 | book | 3 |
| 1 | 1 | journal | 1 |
| 2 | 1 | journal | 2 |
| 10 | 1 | journal | 3 |
| 12 | 1 | journal | 4 |
| 16 | 1 | journal | 5 |
| 4 | 2 | book | 1 |
| 14 | 2 | book | 2 |
| 5 | 2 | journal | 1 |
| 7 | 2 | journal | 2 |
| 13 | 2 | journal | 3 |
| 15 | 2 | journal | 4 |
| 8 | 3 | book | 1 |
| 17 | 3 | book | 2 |
| 18 | 3 | book | 3 |
| 19 | 3 | book | 4 |
| 20 | 3 | book | 5 |
| 11 | 3 | journal | 1 |
+----+--------+---------+------+
Note that MyISAM actually lets you use a composite PK in which part of that composite is an auto-incrementing id, but InnoDB prohinits this.
On larger datasets a query along these lines will likely be far more efficient...
SELECT id
, userid
, item
, IF(#userid=userid,IF(#item=item,#i:=#i+1,#i:=1),#i:=1) rank
, #userid := userid
, #item := item
FROM test
, (SELECT #userid = NULL,#item:='',#i:=1) vars
ORDER
BY userid,item,id;

SQL - Query multiple rooms with different Adults/Children per hotel

I have a simple query where I select available x Rooms with x Adults + x Children per hotel that matches a date range, but I'm having a hard time trying to figure out how to query a list of rooms per hotel like this:
1 Room with 2 Adults / 0 Children
1 Room with 4 Adults / 2 Children
1 Room with 2 Adults / 1 Children
Here is my query:
SELECT COUNT(pl.day) AS Days,
p.property_ID AS Hotel_ID,
p.name AS Hotel_Name,
r.room_name AS Room_Name,
r.room_type_ID AS Room_ID
FROM property p
INNER JOIN room_type r ON p.property_ID=r.property_ID
AND (r.max_adults >= 3
AND r.max_children >= 0)
INNER JOIN plan pl ON pl.room_type_ID=r.room_type_ID
AND (pl.day >= "2014-07-07"
AND pl.day <= "2014-07-11")
GROUP BY Room_ID,
Hotel_ID HAVING Days = 4
EDIT
How do I add 'No_of_Room' in SELECT that differentiates the room_types by the room number, example result of a single room:
Array
(
[Room_Price] => 160.00
[Days] => 4
[Hotel_ID] => 1
[Hotel_Name] => Hotel Alfa
[Room_Name] => Room type C
[Room_ID] => 3
[Max_Adults] => 3
[Max_Children] => 1
[No_of_Room] => 1 // What number of room does this room_type belongs to
)
Then I can show the results like:
EDIT
Rooms table
Rooms(
ID,
hotel_id
room_name,
max_Adults,
max_Children
);
-- Populate
INSERT INTO Rooms VALUES (1,1,"Room A",2,1),(2,1,"Room B",2,5),(3,1,"Room C",3,0);
INSERT INTO Rooms VALUES (1,2,"Room A",2,1),(2,2,"Room B",2,5),(3,3,"Room C",3,4);
EXAMPLES OF USING VIEWS TO MAKE THINGS NICER.
For this project authors may have aliases, for example one book may have "S. Lang" as the author, another might have "Serge Lang", the primary author is the main form (Serge Lang) and the secondaries are things like "S. Lang".
It is important to relate these, ideally I'd like a table with "AuthorId" and "PrimaryAuthorId" as columns, that way I could just select PrimaryAuthorId from it on AuthorId being equal to something.
To do this the view is defined as:
select
`BookSystem_AuthorList`.`AuthorId` AS `AuthorId`,
if((`BookSystem_AuthorList`.`duplicateOf` = 0),
`BookSystem_AuthorList`.`AuthorId`,
`BookSystem_AuthorList`.`duplicateOf`
) AS `PrimaryAuthorId`
from `BookSystem_AuthorList`;
Then
SELECT PrimaryAuthorId FROM BookSystem_PrimaryAuthorId WHERE AuthorId=10;
gives:
7
Which is much nicer for joining!
I then use this view to define another view (EditionAuthorsWithPrimaryId) - this gets the authors of an edition - and the primary author (I can then join to get names as needed)
select
`BookSystem_EditionAuthors`.`BindingId` AS `BindingId`,
`BookSystem_EditionAuthors`.`EditionId` AS `EditionId`,
`BookSystem_EditionAuthors`.`AuthorId` AS `AuthorId`,
`BookSystem_EditionAuthors`.`Position` AS `Position`,
(select
`BookSystem_PrimaryAuthorId`.`PrimaryAuthorId`
from `BookSystem_PrimaryAuthorId`
where (`BookSystem_PrimaryAuthorId`.`AuthorId` = `BookSystem_EditionAuthors`.`AuthorId`)
) AS `PrimaryAuthorId`
from `BookSystem_EditionAuthors`;
Now I can do:
SELECT * FROM BookSystem_EditionAuthorsWithPrimary WHERE EditionId=10;
BindingId, EditionId, AuthorId, Position, PrimaryAuthorId
10, 10, 10, 0, 7
Much nicer!
this next query is a great example
select
`BookSystem_BookList`.`BookId` AS `Id`,
`BookSystem_BookList`.`Title` AS `Name`,
`BookSystem_BookList`.`UserId` AS `UserId`,
`BookSystem_BookList`.`BookType` AS `Subtype`,
1 AS `IsBook`,0 AS `IsSeries`,
0 AS `IsAuthor`
from `BookSystem_BookList`
union
select
`BookSystem_SeriesList`.`SeriesId` AS `Id`,
`BookSystem_SeriesList`.`SeriesName` AS `Name`,
`BookSystem_SeriesList`.`UserId` AS `UserId`,
'' AS `Subtype`,
0 AS `IsBook`,
1 AS `IsSeries`,
0 AS `IsAuthor`
from `BookSystem_SeriesList`
union
select
`BookSystem_AuthorList`.`AuthorId` AS `Id`,
concat(
`BookSystem_AuthorList`.`AuthorSurname`,', ',`BookSystem_AuthorList`.`AuthorForename`,
ifnull(
(select concat(
' (AKA: ',
group_concat(
concat(
`BookSystem_AuthorList`.`AuthorSurname`,
', ',
`BookSystem_AuthorList`.`AuthorForename`
) separator '; '
),')'
) AS `AKA` from `BookSystem_AuthorList`
where
(`BookSystem_AuthorList`.`duplicateOf` = `Id`)
group by (`BookSystem_AuthorList`.`duplicateOf` = `Id`)
),'')) AS `Name`,
`BookSystem_AuthorList`.`UserId` AS `UserId`,
'' AS `SubType`,
0 AS `IsBook`,
0 AS `IsSeries`,
1 AS `IsAuthor`
from `BookSystem_AuthorList`
where (`BookSystem_AuthorList`.`duplicateOf` = 0) order by `Name`;
IS HUGE!
But now I can get all the things for UserId=1 easily:
mysql> SELECT * FROM BookSystem_Index WHERE UserId = 1;
+----+----------------------------------------+--------+-------------+--------+----------+----------+
| Id | Name | UserId | Subtype | IsBook | IsSeries | IsAuthor |
+----+----------------------------------------+--------+-------------+--------+----------+----------+
| 4 | A First Course in Calculus | 1 | Normal | 1 | 0 | 0 |
| 2 | A First Course in Real Analysis | 1 | Normal | 1 | 0 | 0 |
| 2 | Algebra | 1 | | 0 | 1 | 0 |
| 13 | Analysis II assignments | 1 | Assignments | 1 | 0 | 0 |
| 14 | Author Test | 1 | Normal | 1 | 0 | 0 |
| 8 | b, g | 1 | | 0 | 0 | 1 |
| 7 | b, g (AKA: t, lll; Teal, lll) | 1 | | 0 | 0 | 1 |
| 1 | Calculus of Several Variables | 1 | Normal | 1 | 0 | 0 |
| 4 | DuBois, Paul | 1 | | 0 | 0 | 1 |
| 1 | Lang, Serge (AKA: Lang, S. E. R. G. E) | 1 | | 0 | 0 | 1 |
| 5 | Linear Algebra | 1 | Normal | 1 | 0 | 0 |
| 3 | Morrey, C. B. | 1 | | 0 | 0 | 1 |
| 6 | MySQL | 1 | Normal | 1 | 0 | 0 |
| 7 | Principles of Mathematical Analysis | 1 | Normal | 1 | 0 | 0 |
| 2 | Protter, M. H. | 1 | | 0 | 0 | 1 |
| 5 | Rudin, Walter | 1 | | 0 | 0 | 1 |
| 10 | t | 1 | Normal | 1 | 0 | 0 |
| 3 | Test | 1 | | 0 | 1 | 0 |
| 12 | Test 1 | 1 | Normal | 1 | 0 | 0 |
| 11 | Test 4.4.2014 | 1 | Normal | 1 | 0 | 0 |
| 8 | Topology and Analysis | 1 | Normal | 1 | 0 | 0 |
| 3 | Undergraduate Algebra | 1 | Normal | 1 | 0 | 0 |
| 1 | Undergraduate Texts in Mathematics | 1 | | 0 | 1 | 0 |
| 9 | w | 1 | Normal | 1 | 0 | 0 |
+----+----------------------------------------+--------+-------------+--------+----------+----------+
24 rows in set (0.00 sec)
The optimiser sees the view properly, it wont generate the full view, it effectively substitutes the required selects.
(Taken from a testing DB, not production, hence weird names like "TESTING")
First, the room type selection needs to be framed correctly. The following join would probably work.
EDIT:
The query has been edited to return only properties with all three room types. It has also been joined with the plan table.
SELECT
COUNT(pl.day) AS Days,
p.property_ID AS Hotel_ID,
p.name AS Hotel_Name,
r.room_name AS Room_Name,
r.room_type_ID AS Room_ID,
r.max_adults as Max_Adults,
r.max_children as Max_Children
FROM property p
INNER JOIN room_type r
ON p.property_ID=r.property_ID
INNER JOIN plan pl
ON pl.room_type_ID=r.room_type_ID
AND (pl.day >= '2014-07-07' AND pl.day <= '2014-07-11')
WHERE EXISTS
(SELECT 1
FROM room_type r1
WHERE p.property_ID=r1.property_ID
AND r1.max_adults = 2 AND r1.max_children = 0)
AND EXISTS
(SELECT 1
FROM room_type r2
WHERE p2.property_ID=r2.property_ID
AND r2.max_adults = 4 AND r2.max_children = 2)
AND EXISTS
(SELECT 1
FROM room_type r3
WHERE P.PROPERTY_ID=R3.PROPERTY_ID
AND r3.max_adults = 2 AND r3.max_children = 1)
GROUP BY
p.property_ID,
p.name,
r.room_name,
r.room_type_ID,
r.max_adults,
r.max_children
HAVING
COUNT(pl.day) = 4;