How to correctly convert SQL rows to columns? - mysql

Before someone mentions it, I have seen the exact same SQL question on Stack before and from my point of view, that's actually transposing columns to rows. What I'm trying to accomplish is as seen below in the photos, given the top one, I want to create a new table with the previous data that is flipped in this sense.

You can use the conditional aggregation and union all as follows:
select name_new,
max(case when name = 'PersonA' then A end) as PersonA,
max(case when name = 'PersonB' then A end) as PersonB,
max(case when name = 'PersonC' then A end) as PersonC
from
(select name, 'A1' name_new, A1 A from mytable union all
select name, 'A2' name_new, A2 A from mytable union all
select name, 'A3' name_new, A3 A from mytable union all
select name, 'A4' name_new, A4 A from mytable ) t
group by name_new
SQLFiddle

Related

How to use group by function for more columns

I have used following query to get below output from my exisiting database.
select date(RaisedTime) as date, object,User,Count(*) as total from table1 where object like '%Object%' and User in ('User1','User2','User3','User4','User5','User6') group by date(RaisedTime),Object,User;
The result is what I needed but not the way I need it. I need to show this with much analyzed way such as below,
Can someone help me to do what I need?
SELECT DATE(RaisedTime) AS `date`,
Object,
SUM(User = 'User1') AS User1,
-- ...
SUM(User = 'User6') AS User6
FROM table1
WHERE Object LIKE '%Object%'
AND User IN ('User1','User2','User3','User4','User5','User6')
GROUP BY DATE(RaisedTime), Object;
select `date`,users,revenue, max(case when seq = 1 then object end) objA, max(case when seq = 2 then object end) objB, max(case when seq = 3 then object end) objC from (select `date`, object, users,revenue, row_number() over(partition by `date` order by `date`) seqfrom UserAnalysis ) d group by `date`;
You can use row_number() to analyze the result in a proper way. Please refer to this image for the result output. SQL Result

Need help DB Query for this scenario

I am unable to derive a SQL query for the following table content.
When i tried below query i am getting above said output. Can someone help me to give the required query for it.
select Name, count(Status) from mytable where Status='Open' group by mytable union
select Name, count(Status) from mytable where Status='Cleared' group by mytable
Use case expressions in the select list to do conditional aggregation.
select Name,
count(case when Status = 'Open' then 1 end) as opencnt,
count(case when Status = 'Cleared' then 1 end) as clearedcnt
from mytable
where Status in ('Open', 'Cleared')
group by Name
COUNT() counts non-null values. The case expressions above returns null when the conditions aren't fulfilled.

How to select rows as a column for View in TSQL?

Assume I have 3 tables: Animal, CareTaker, and Apppointment. Schema, with some data like so:
Create Table Animal (Id int identity, Name varchar(25))
Create Table CareTaker(Id int identity, Name varchar(50))
Create Table Appointments(Id int identity, AnimalId int, CareTakerId int, AppointmentDate DateTime, BookingDate DateTime)
Insert into Animal(Name) Values('Ghost'), ('Nymeria'), ('Greywind'), ('Summer')
Insert into CareTaker(Name) Values ('Jon'), ('Arya'), ('Rob'), ('Bran')
Insert into Appointments(AnimalId, CareTakerId, AppointmentDate, BookingDate) Values
(1, 1, GETDATE() + 7, GetDate()), -- Ghost cared by Jon
(1, 2, GETDATE() + 6, GetDate()), -- Ghost cared by Arya
(4, 3, GETDATE() + 8, GetDate()) -- Summer cared by Rob
I want to select only 3 caretakers for each animal as a columns. Something like this:
I don't care about other appointments, just the next three, for each animal. If there aren't three appointments, it can be blank / null.
I'm quite confused about how to do this.
I tried it with Sub queries, something like so:
select Name,
-- Care Taker 1
(Select Top 1 C.Name
From Appointments A
Join CareTaker C on C.Id = A.CareTakerId
Where A.AppointmentDate > GETDATE()
And A.AnimalId = Animal.Id
Order By AppointmentDate) As CareTaker1,
-- Appointment Date 1
(Select Top 1 AppointmentDate
From Appointments
Where AppointmentDate > GETDATE()
And AnimalId = Animal.Id
Order By AppointmentDate) As AppointmentDate1
From Animal
But for the second caretaker, I would have to go second level select on where clause to exclude the id from top 1 (because not sure how else to get second row), something like select top 1 after excluding first row id; where first row id is (select top 1) situtation.
Anyhow, that doesn't look like a great way to do this.
How can I get the desired output please?
You can get all the information in rows using:
select an.name as animal, ct.name as caretaker, a.appointmentdate
from appointments a join
animals an
on a.id = an.animalid join
caretaker c
on a.caretakerid = c.id;
Then, you basically want to pivot this. One method uses the pivot keyword. Another conditional aggregation. I prefer the latter. For either, you need a pivot column, which is provided using row_number():
select animal,
max(case when seqnum = 1 then caretaker end) as caretaker1,
max(case when seqnum = 1 then appointmentdate end) as appointmentdate1,
max(case when seqnum = 2 then caretaker end) as caretaker2,
max(case when seqnum = 2 then appointmentdate end) as appointmentdate2,
max(case when seqnum = 3 then caretaker end) as caretaker3,
max(case when seqnum = 3 then appointmentdate end) as appointmentdate3
from (select an.name as animal, ct.name as caretaker, a.appointmentdate,
row_number() over (partition by an.id order by a.appointmentdate) as seqnum
from appointments a join
animals an
on a.id = an.animalid join
caretaker c
on a.caretakerid = c.id
) a
group by animal;

Transpose simple single column 3-row results into single row, 3-column one?

I have a statistical query that would return three rows (as I have 3 types by which I group by), I also know the order of the rows as I do explicit ORDER BY FIELD:
SELECT COUNT(id) AS c FROM Vehicles GROUP BY VehicleTypeID ORDER BY FIELD(VehicleTypeID, 1,2,3)
Is there a simple way to transpose the rows into columns? Something like (PSEUDO SQL):
SELECT c[0] AS CarsCount, c[1] AS MotorcyclesCount, c[2] AS TrucksCount FROM (
SELECT COUNT(id) AS c
FROM Vehicles
GROUP BY VehicleTypeID
ORDER BY FIELD(VehicleTypeID, 1,2,3)
)
Yes, it is a case statement with aggregation:
SELECT max(case when fieldnum = 1 then c end) AS CarsCount,
max(case when fieldnum = 2 then c end) AS MotorcyclesCount,
max(case when fieldnum = 3 then c end) AS TrucksCount
FROM (SELECT COUNT(id) AS c , FIELD(VehicleTypeID, 1,2,3) as fieldnum
FROM Vehicles
GROUP BY VehicleTypeID
) t;

temporary table has a count for each other table

I'm trying to make a statistics page in my php script. in order to select the count from each table I need more than 30 Queries like this
SELECT COUNT(order_id) as `uncompleted_orders` FROM `orders` WHERE `order_status` != 0
and then I need to run another query like this:
SELECT COUNT(order_id) as `completed_orders` FROM `orders` WHERE `order_status` = 1
I've tried this approach, but it didn't work:
SELECT COUNT(order_id) as `uncompleted_orders` FROM `sd_orders` WHERE `order_status` != 4;
SELECT COUNT(order_id) as `completed_orders` FROM `sd_orders` WHERE `order_status` = 4;
Is there any way to creat a new temp table in MySQL contains the count for other tables?
You could try something like this:
SELECT
(
SELECT COUNT(order_id) FROM `sd_orders` WHERE `order_status` != 4
) as `uncompleted_orders`,
(
SELECT COUNT(order_id) FROM `sd_orders` WHERE `order_status` = 4
) as `completed_orders`
You will have a result set with one row and a field for each count.
Without more information it's impossible to generalise, but there are many constructs that can help you here.
First, your example is actually from one table, and not two. This means that you can do the following...
SELECT
COUNT(CASE WHEN order_status = 4 THEN order_id END) AS complete_orders,
COUNT(CASE WHEN order_status <> 4 THEN order_id END) AS incomplete_orders
FROM
sd_orders
This works because COUNT(<something>) doesn't include an NULLs in the results. And by not including an ELSE clause, anything that doesn't match returns NULL. Another way people accomplish the same result is SUM(CASE WHEN ? THEN 1 ELSE 0 END).
Second, where you do actually have multiple tables, you can combine the results in several different ways...
-- Where you want one value from each table...
--------------------------------------------------------------------------------
SELECT
(SELECT COUNT(*) FROM table1 WHERE fieldx = ?) AS value1,
(SELECT COUNT(*) FROM table2 WHERE fieldy = ?) AS value2
-- Where you want one row of values from each table...
--------------------------------------------------------------------------------
SELECT
table1_summary.value1 AS table1_value1,
table1_summary.value2 AS table1_value2,
table2_summary.value1 AS table2_value1,
table2_summary.value2 AS table2_value2
FROM
(
SELECT
COUNT(CASE WHEN fieldx = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldx <> ? THEN id END) AS value2
FROM
table1
)
AS table1_summary
CROSS JOIN
(
SELECT
COUNT(CASE WHEN fieldy = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldy <> ? THEN id END) AS value2
FROM
table2
)
AS table2_summary
-- Where you want many rows, but of the same fields, from each table...
--------------------------------------------------------------------------------
SELECT
*
FROM
(
SELECT
'Table1' AS source_table,
fielda AS some_grouping,
COUNT(CASE WHEN fieldx = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldx <> ? THEN id END) AS value2
FROM
table1
GROUP BY
fielda
UNION ALL
SELECT
'Table2' AS source_table,
fieldb AS some_grouping,
COUNT(CASE WHEN fieldy = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldy <> ? THEN id END) AS value2
FROM
table2
GROUP BY
fieldb
)
AS summary
ORDER BY
source_table,
some_grouping,
value1,
value2
As you can see, there are a lot of ways to do this. How you approach it totally depends on your data and your needs.