How to show pivot data without directly using pivot statement? - mysql

I am trying to pivoting the data without directly using the pivot function.
I have a simple table t1 which has:
ID Employee Name
100 Amit
100 Rohan
101 Rohit
102 Pradnya
My expected output is:
100 101 103
2 1 1
I want to achieve this without using pivot. I tried using:
SELECT *
FROM (SELECT CASE
WHEN id = '101' THEN '101'
END,
CASE
WHEN id = '102' THEN '102'
END,
CASE
WHEN id = '103' THEN '103'
END,
Count(*) cnt
FROM t1
GROUP BY CASE
WHEN id = '101' THEN '101'
END,
CASE
WHEN id = '102' THEN '102'
END,
CASE
WHEN id = '102' THEN '102'
END);
How can I achieve the output without pivot?

You are sort of there, try the following:
with s as (
select id, Count(*) cnt
from t
group by id
)
select
max(case when id=100 then cnt end) as '100',
max(case when id=101 then cnt end) as '101',
max(case when id=102 then cnt end) as '102'
from s
See Example Fiddle

Related

Join not summing CASE WHEN

What I'm looking to do is show my current performance for this month, compared with expected scheduled wins to come in and then display the total expected amount, by product type.
For clarity, I have two sub-products that I'm grouping under the same name.
My issue is that for my 'Charged' amount, it's keeping the two sub-products separate, where as the 'Scheduled' amount is working fine.
The table should look like:
Type | Charged | Scheduled | Expected
A 3 2 5
B 1 1 2
What's actually showing is:
Type | Charged | Scheduled | Expected
A 2 1 3
A 1 1 2
B 1 1 2
The code is as follows:
select
t2.product,
t1.Charged,
t2.Scheduled,
t1.charged + t2.scheduled as 'expected'
from(
select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as 'Type',
SUM(charged) as 'Scheduled'
from
table
where
month(date) = month(now())
and
year(date) = year(now())
and status like 'scheduled'
group by 1
order by 2 desc) t2 join
(select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as 'Type',
sum(charged) as 'Charged'
FROM table
WHERE (status = 'Complete'
AND str_to_date(concat(date_format(date, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d'))
GROUP BY user_type
ORDER BY user_type ASC) as t1 on t1.type = t2.type
I appreciate I might not be explaining this incredibly well (and that my code is probably quite clunky - I'm still fairly new!) so any help/direction would be appreciated.
Thanks!
Just some suggestion
you have a column product in main select but you have type in subquery and not product
you should not use sigle quote around column name
ad you have group by user_type but you need group by type for charged
select
t2.type,
t1.Charged,
t2.Scheduled,
t1.charged + t2.scheduled as 'expected'
from(
select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as Type,
SUM(charged) as Scheduled
from
table
where
month(date) = month(now())
and
year(date) = year(now())
and status like 'scheduled'
group by 1
order by 2 desc) t2 join
(select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as Type,
sum(charged) as Charged
FROM table
WHERE (status = 'Complete'
AND str_to_date(concat(date_format(date, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d'))
GROUP BY Type
ORDER BY Type ASC) as t1 on t1.type = t2.type

Select sum with a group by inside a group by

With this example iformation table:
How can i output this information?
I'm trying this query,
but it's just returning me the total number of 'PART' rows for each 'NAMES'.
SELECT
NAMES
, SUM(PART = "F001") AS SUM_F001
, SUM(PART = "F002") AS SUM_F002
, SUM(PART = "F003") AS SUM_F003
FROM
MY_TABLE
GROUP BY NAMES ASC
You are pretty close with your current query.
But you need to use the query below to correctly pivot.
SELECT
NAMES
, MAX(CASE WHEN PART = 'F001' THEN QTY ELSE 0 END) AS F001
, MAX(CASE WHEN PART = 'F002' THEN QTY ELSE 0 END) AS F002
, MAX(CASE WHEN PART = 'F003' THEN QTY ELSE 0 END) AS F003
, SUM(QTY) AS alias
FROM
FROM
MY_TABLE
GROUP BY
NAMES # Don't use ASC OR DESC on GROUP BY because it's deprecated
ORDER BY
NAMES ASC
U can use the query as follows
SELECT NAMES , SUM(CASE WHEN PART = 'F001' THEN 1 ELSE 0 END) AS F001 , SUM(CASE WHEN PART = 'F002' THEN 1 ELSE 0 END) AS F002 , SUM(CASE WHEN PART = 'F003' THEN 1 ELSE 0 END) AS F003 , SUM(QTY) AS alias FROM FROM MY_TABLE GROUP BY NAMES ORDER BY NAMES ASC

How to select count(distinct) with additional where clause in MySQL?

I have a query that looks like this:
SELECT clicks.offer_id, count(distinct clicks.transaction_id) as unique_clicks
FROM clicks
WHERE date > '2014-12-01 17:43:30' and offer_id = 1;
This query counts the number of unique clicks by their transaction ID for an offer.
Now, I want to add the unique number of clicks for mobile and desktop users. Along the lines of:
SELECT clicks.offer_id, count(distinct clicks.transaction) as unique_clicks,
sum(case when clicks.mobile = 1 then 1 else 0 end) as mobile_unique_clicks,
sum(case when clicks.mobile = 0 then 1 else 0 end) as desktop_unique_clicks
FROM clicks
WHERE date > '2014-12-01 17:43:30' and offer_id = 1;
The problem with this is that it won't count unique transaction IDs. Is there a way to do this with one query? And what kind of covering indexes would I need to keep this efficient?
SELECT clicks.offer_id, count(distinct clicks.transaction_id) as unique_clicks,
count(DISTINCT case when clicks.mobile = 1 then clicks.transaction_id else null end) as mobile_unique_clicks,
count(DISTINCT case when clicks.mobile = 0 then clicks.transaction_id else null end) as desktop_unique_clicks
FROM clicks
WHERE date > '2014-12-01 17:43:30' and offer_id = 1;
You can use COUNT(DISTINCT ...)
something like this?
SELECT clicks.offer_id,
count(distinct clicks.transaction_id) as unique_clicks,
sub.mobile_unique_clicks,
count(distinct clicks.transaction_id) - sub.mobile_unique_clicks as desktop_unique_clicks
FROM clicks
JOIN
(
SELECT clicks.offer_id, count(distinct clicks.transaction_id) as mobile_unique_clicks,
FROM clicks
WHERE date > '2014-12-01 17:43:30' and offer_id = 1 AND clicks.mobile = 1
) sub
WHERE date > '2014-12-01 17:43:30' and offer_id = 1;
If I'm understanding your question correctly, you can move your query to a subquery and use distinct:
select offer_id,
count(transaction) unique_clicks,
count(case when mobile = 1 then 1 end) mobile_unique_clicks,
count(case when mobile = 0 then 1 end) desktop_unique_clicks
from (
select distinct offer_id, transaction, mobile
from clicks
where date > '2014-12-01 17:43:30' and offer_id = 1
) t
group by offer_id

SQL query - print the min and max from one table in the same row

I need a little help with an sql query.
I have a table with a format and data that looks like this:
id | applicant_id | application_status | status_time
1 | 1234 | received | 2013-05-06 15:00:00
1 | 1234 | pending | 2013-05-06 15:30:00
1 | 1234 | approved | 2013-05-06 16:00:00
The problem that I need to solve will have to print the following:
applicant_id | initial_status | initial_time | current_status | current_status_time
1234 | received | 2013-05-06 15:00:00 | approved | 2013-05-06 16:00:00
How could I go about accomplishing something like this, preferably only using joins and no nested selects?
The best way to approach this, in general, is to use the row_number() function. However, this requires a nested select:
select t.applicant_id,
max(case when seqnum_asc = 1 then status end) as initial_status,
max(case when seqnum_asc = 1 then status_time end) as initial_time,
max(case when seqnum_desc = 1 then status end) as current_status,
max(case when seqnum_desc = 1 then status_time end) as current_time
from (select t.*,
row_number() over (partition by applicant_id order by status_time) as seqnum_asc,
row_number() over (partition by applicant_id order by status_time desc) as seqnum_desc
from t
) t
group by t.applicant_id;
If your database did not support row_number(), I would recommend correlated subqueries, for readability. But those are also nested. Here is a solution in MySQL that meets your requirements:
select t.applicant_id,
substring_index(group_concat(status) separator ',' order by status_time), ',', 1) as initial_status,
min(status_time) as initial_time,
substring_index(group_concat(status) separator ',' order by status_time desc), ',', 1) as current_status,
max(status_time) as current_time
from t
group by t.applicant_id;
You did not state your database product, but you could use something like this on any database:
select t1.id,
t1.applicant_id,
max(case when t1.status_time = t2.mintime then t1.application_status end) initial_status,
max(case when t1.status_time = t2.mintime then t1.status_time end)initial_time,
max(case when t1.status_time = t2.maxTime then t1.application_status end) current_status,
max(case when t1.status_time = t2.maxTime then t1.status_time end) `current_time`
from yourtable t1
inner join
(
select id, applicant_id,
max(status_time) maxTime,
min(status_time) mintime
from yourtable
group by id, applicant_id
) t2
on t1.id = t2.id
and t1.applicant_id = t2.applicant_id
and
(
t1.status_time = t2.mintime
or t1.status_time = t2.maxtime
)
group by t1.id, t1.applicant_id;
See SQL Fiddle with Demo
SELECT a.application_id
, a.application_status as initial_status
, a.status_time as initial_time
, b.application_status as current_status
, b.status_time as current_status_time
FROM sample1 A
CROSS JOIN sample1 B
WHERE A.application_status = 'received'
and b. application_status = 'approved'
Assuming MS SQL (Transact-SQL), and that your source table is aptly named [SourceTable]. =)
SELECT DISTINCT
[Probe].applicant_id,
[LogMin].application_status [initial_status],
[LogMin].status_time [initial_time],
[LogMax].application_status [current_status],
[LogMax].status_time [current_status_time]
FROM (
SELECT MAX(status_time) [MaxDate],
MIN(status_time) [MinDate],
[applicant_id]
FROM [SourceTable]
GROUP BY [applicant_id]
) [Probe]
INNER JOIN [SourceTable] [LogMax]
ON [Probe].[applicant_id] = [LogMax].[applicant_id]
AND [Probe].[MaxDate] = [LogMax].[status_time]
INNER JOIN [SourceTable] [LogMin]
ON [Probe].[applicant_id] = [LogMin].[applicant_id]
AND [Probe].[MinDate] = [LogMin].[status_time]
Link to the SQLFiddle test is here.
Assuming that for one applicant_id you have one line for 'received' status and also one line for 'approved' status (as you listed in the question) you can use inline views to solve your issue:
select section1.applicant_id AS applicant_id, 'received' AS initial_status,
section1.status_time AS initial_time, 'approved' AS current_status,
section2.status_time AS current_status_time from
(select applicant_id, status_time from yourtable where application_status = 'received') section1,
(select applicant_id, status_time from yourtable where application_status = 'approved') section2
where section1.applicant_id = section2.applicant_id;
Try something like this.
select
t1.applicant_id,
t2.application_status initial_status,
t1.initial_time,
t3.application_status current_status,
t1.current_status_time
from
(select
applicant_id,
min(status_time) initial_time,
max(status_time) current_status_time
from
your_table
group by
applicant_id) t1
inner join your_table t2
on (t1.applicant_id = t2.applicant_id and t1.initial_time = t2.status_time)
inner join your_table t3
on (t1.applicant_id = t3.applicant_id and t1.current_status_time = t3.status_time)

MySQL Compare Rows with empty entries

I've read a lot of the examples on self join, but they don't seem to cover the case where some fields are not in some rows.
For eg, I have a database with:
testId, testItem, testResult
And the rows:
1,test1,1
1,test2,0
1,test3,1
2,test1,0
2,test4,1
2,test5,1
I would like the output:
testItem,a.testId,b.testId,a.testResult,b.testResult
test1,1,2,1,0
test2,1,NULL,0,NULL
test3,1,NULL,1,NULL
test4,NULL,2,NULL,1
test5,NULL,2,NULL,1
Essentially, I want to compare each testItem (test1->test5) from two different testIds (1 and 2) and compare their testResult values, factoring in testIds that may not have the same test Items.
Given your exact requirement, you can try this:
select testItem
, max(case when testID = 1 then testID else null end) as testID1
, max(case when testID = 2 then testID else null end) as testID2
, max(case when testID = 1 then testResult else null end) as testResult1
, max(case when testID = 2 then testResult else null end) as testResult2
from mytable
where testID in (1,2)
group by testItem
This makes a lot of assumptions about your data, so take it with a grain of salt.
It looks like you want a FULL OUTER JOIN, which is not supported in MySQL. You can emulate this with a UNION of two queries: a LEFT JOIN query and RIGHT JOIN which throws out matching rows.
Something like this will return the specified resultset:
SELECT a.testItem
, a.testId AS `a.testId`
, b.testId AS `b.testId`
, a.testResult AS `a.testResult`
, b.testResult AS `b.testResult`
FROM mytable a
LEFT
JOIN mytable b
ON b.testItem = a.testItem
AND b.testId = 2
WHERE a.testId = 1
AND a.testItem IN ('test1','test2','test3','test4','test5')
UNION ALL
SELECT d.testItem
, c.testId
, d.testId
, c.testResult
, d.testResult
FROM mytable d
LEFT
JOIN mytable c
ON c.testItem = d.testItem
AND c.testId = 1
WHERE d.testId = 2
AND d.testItem IN ('test1','test2','test3','test4','test5')
AND c.testId IS NULL
ORDER
BY 1,2,4
(I included the predicates on testItem IN ('test1' thru 'test5') because you specified that as a requirement; those predicates could be removed if you want all values for testItem included.)
SQLFiddle Demo
select testItem,
group_concat(IFNULL(testId,'null') separator ', ') testIds,
group_concat(IFNULL(testResult, 'null') separator ', ') testResults
from table_name group by testItem;