Unpivoting 2 columns - sql-server-2008

This is my sample table:
CREATE TABLE TestSample
(
Id INT ,
StudyStartDate DATETIME ,
StudyEndDate DATETIME ,
DesignStartDate DATETIME ,
DesignEndDate DATETIME ,
StudyStartDiff INT ,
StudyEndDiff INT ,
DesignStartDiff INT ,
DesignEndDiff INT
)
INSERT INTO TestSample
VALUES (1, '2010-04-22', '2012-01-17', '2012-04-09', '2012-10-08', 635, 635, 182, 182)
INSERT INTO TestSample
VALUES (2, '2014-04-15', '2015-04-30', '2015-03-30', '2016-04-11', 380, 380, 378, 378)
SELECT *
FROM TestSample
I need to unpivot this table to bring StudyStartDate and StudyStartDatediff under a single Milestone code and similarly the other columns
So my output should look like :
ID Mcode Mdate Variance
1 XY105 2010-04-22 635
1 XY110 2012-01-17 635
1 XY205 2012-04-09 182
1 XY210 2012-10-08 182
2 XY105 2014-04-15 380
2 XY110 2015-04-30 380
2 XY205 2015-03-30 378
2 XY210 2016-04-11 378
The logic behind this is
For MCode XY105 : The Mdate should be StudyStartDate & Variance :StudyStartDiff
For MCode XY110 : The Mdate should be StudyEndDate & Variance :StudyEndDiff
For MCode XY205 : The Mdate should be DesignStartDate & Variance :DesignStartDiff
For MCode XY110 : The Mdate should be DesignEndDate & Variance :DesignEndDiff**
Can anyone help me with this query ?

If you are not required to use UNPIVOT, would this do what you need to do?
SELECT ID, 'XY105' AS Mcode, ts.StudyStartDate AS Mdate, ts.StudyStartDiff AS Variance FROM dbo.TestSample AS ts
UNION
SELECT ID, 'XY110' AS Mcode, ts.StudyEndDate AS Mdate, ts.StudyEndDiff AS Variance FROM dbo.TestSample AS ts
UNION
SELECT ID, 'XY205' AS Mcode, ts.DesignStartDate AS Mdate, ts.DesignStartDiff AS Variance FROM dbo.TestSample AS ts
UNION
SELECT ID, 'XY210' AS Mcode, ts.DesignEndDate AS Mdate, ts.DesignEndDiff AS Variance FROM dbo.TestSample AS ts

select id,Dates1,Value1 from(
select Id,
StudyStartDate,StudyStartDiff,
StudyEndDate, StudyEndDiff,
DesignStartDate,DesignStartDiff,
DesignEndDate ,DesignEndDiff
from TestSample
) as Ts
unpivot
(Dates1 FOR Dates IN ( StudyStartDate,StudyEndDate, DesignStartDate,DesignEndDate)
) AS mrks
unpivot
(Value1 FOR Value IN ( StudyStartDiff,StudyEndDiff,DesignStartDiff,DesignEndDiff)
) AS Value
WHERE Left(Value,10) = Left(Dates,10)

Related

mysql Select duplicate rows(based on a column) for each id (not unique)

Imagine a table as below.
secId sedol mic
258 887754 XHKG
258 B0CRGS8 XPFT
258 B10RVH1 XNCD
258 B709630 XNCD
258 BK9YH34 XFRG
471 BF1SPP7 XHYF
471 BDCCPF4 XHYF
471 TK3YH32 XJTE
857 BG4YC54 XHEV
857 BS5YS63 XYEC
857 BK9YH64 XUTE
857 BU2YR21 XAVT
I want result as:
secId sedol mic
258 B10RVH1 XNCD
258 B709630 XNCD
471 BF1SPP7 XHYF
471 BDCCPF4 XHYF
For each unique secId I want to select rows where mic column value is not unique.
On MySQL 8+, analytic functions make this easy to do:
WITH cte AS (
SELECT *, COUNT(*) OVER (PARTITION BY secId, mic) cnt
FROM yourTable
)
SELECT secId, sedol, mic
FROM cte
WHERE cnt >= 2;
On earlier versions of MySQL, we can try joining to a subquery which finds the counts for each secId/mic group:
SELECT t1.secId, t1.sedol, t1.mic
FROM yourTable t1
INNER JOIN
(
SELECT secId, mic
FROM yourTable
GROUP BY secId, mic
HAVING COUNT(*) >= 2
) t2
ON t2.secId = t1.secId AND
t2.mic = t1.mic;
Your table:
use test;
drop table if exists mytable;
create table mytable (secId int, sedol varchar(34), mic char(20));
insert into mytable values
(258, '887754', 'XHKG'),
(258, 'B0CRGS8', 'XPFT'),
(258, 'B10RVH1', 'XNCD'),
(258, 'B709630', 'XNCD'),
(258, 'BK9YH34', 'XFRG'),
(471, 'BF1SPP7', 'XHYF'),
(471, 'BDCCPF4', 'XHYF'),
(471, 'TK3YH32', 'XJTE'),
(857, 'BG4YC54', 'XHEV'),
(857, 'BS5YS63', 'XYEC'),
(857, 'BK9YH64', 'XUTE'),
(857, 'BU2YR21', 'XAVT');
Following query will do the job:
select
a.secId,
sedol,
a.mic
from
mytable a
join (
select
secId,
mic,
count(mic) miccount
from
mytable
group by
secId,
mic
having
miccount > 1) b on
a.secId = b.secId
and a.mic = b.mic;

grouping records by a field and submit a query on each group in sql

I have a table like this:
create table product_company (
id int PRIMARY KEY,
productName varchar(100),
companyName varchar(100),
price int
);
I want to know the name of the product which it has the second rank in price in each company.
for example if company1 has three product product1=30, product2=50 and product3=15(the assignment shows the price of each product in this company) so product1 has the second rank in price property in company1 and I want to write a query that returns something like below:
company1 product1
company2 ...
...
I mean for every company I want to know the product that has the second rank in price within that company.
I don't know how to use group by clause because group by is working fine by aggregate functions but I don't want the maximum in price.
I want to write this query with standard sql queries and clauses and without some special funcions that may not work in some DBMS
If you are running MySQL 8.0, you can use window function dense_rank():
select *
from (
select
pc.*,
dense_rank() over(partition by companyName order by price desc) rn
from product_company pc
) t
where rn = 2
In earlier versions, one solution is to filter with a correlated subquery. But you have to be careful to properly handle possible top ties. This should do it:
select pc.*
from product_company pc
where (
select count(distinct pc1.price)
from product_company pc1
where pc1.companyName = pc.companyName and pc1.price > pc.price
) = 1
An EXISTS with a COUNT can also be used for this
For example:
create table product_company (
id int PRIMARY KEY AUTO_INCREMENT,
productName varchar(100),
companyName varchar(100),
price decimal(16,2)
);
insert into product_company
(productName, companyName, price) values
('product 1', 'odd org', 9)
,('product 2', 'odd org', 15)
,('product 3', 'odd org', 11)
,('product 4', 'odd org', 17)
,('product 5', 'even inc.', 18)
,('product 6', 'even inc.', 12)
,('product 7', 'even inc.', 16)
,('product 8', 'even inc.', 14)
;
select *
from product_company t
where exists
(
select 1
from product_company t2
where t2.companyName = t.companyName
and t2.price >= t.price
having count(distinct t2.price) = 2
)
id | productName | companyName | price
-: | :---------- | :---------- | ----:
2 | product 2 | odd org | 15.00
7 | product 7 | even inc. | 16.00
db<>fiddle here
And if you want to have the top 2 per company?
Then change the HAVING clause
...
having count(distinct t2.price) <= 2
...

Selecting only max column

I'm trying to select only a max column by grade_level. If I select a column that is less than max column by grade_level should not be selected.
CREATE TABLE sample
(
name VARCHAR(50),
grade_level INT,
money INT
);
INSERT INTO sample (name, grade_level, money) VALUES
("John", 1, 100),
("John", 2, 200),
("John", 2, 250),
("Shadow", 1, 110),
("Shadow", 2, 300);
SELECT * FROM sample;
name | grade_level | money
John 1 100
John 2 200
John 2 250
Shadow 1 110
Shadow 2 300
This is my sample query so far while working on it.
SELECT name
, max(money)
FROM sample
WHERE grade_level = (SELECT max(grade_level) FROM sample WHERE grade_level = 2)
GROUP BY name;
Output:
name | money
John 250
Shadow 110
As you can see here if I put grade_level as 2 in my WHERE CLAUSE it gives me a right value. But how can avoid selecting other column if I put grade_level as 1 because I'm only selecting the max column.
Something simple like this will work :
select name, max(money)
from sample
group by grade_level
Use grade_level in GROUP BY clause instead of name
i think this what you are expecting let me know if i am wrong
SELECT name, max(money),grade_level FROM sample
GROUP BY name,grade_level;
i created a fiddle for your answer check it out : http://sqlfiddle.com/#!9/48022b/4
EDIT :
try this :
SELECT name, max(money) FROM sample
WHERE grade_level = (SELECT max(grade_level) FROM sample)
and grade_level = 2
GROUP BY name;
check the fiddle for more info http://sqlfiddle.com/#!9/48022b/11

MySQL - SELECT query - oldest and youngest date

I have to build one SQL SELECT query. Table structure is as follows:
[ID] [PHONE] [description] [DATE]
[1] [600898367] [main] [2016-01-23]
[2] [600898367] [] [2016-01-24]
[3] [600898367] [] [2016-01-26]
[4] [600898367] [] [2016-01-28]
[5] [662349093] [main] [2016-01-10]
[6] [662349093] [] [2016-01-21]
[7] [662349093] [] [2016-01-30]
[8] [662349093] [] [2016-01-31]
You have here different records grouped within the same telephone number. The first (the oldest) occurance is marked with [main] flag. There's no two identical numbers with [main] flag.
I want to select each [main] record and additionaly one youngest with the same phone number, so the result should give records 1,4,5,8.
Please help.
Use a WHERE clause to give you the records with the main flag. Use MAX to get the most recent record and JOIN to get the additional columns. Finally, do a UNION ALL to combine the result.
-- Get the main records first
SELECT *
FROM tbl
WHERE description = 'main'
UNION ALL
-- Get the most recent records
SELECT b.*
FROM (
SELECT
t.PHONE,
MAX(DATE) AS MaxDate
FROM tbl t
GROUP BY PHONE
) a
INNER JOIN tbl b -- Do a JOIN to get the additional columns
ON b.PHONE = a.PHONE
AND b.DATE = a.MaxDate
Try this;)
SQL Fiddle
MySQL 5.6 Schema:
CREATE TABLE table1
(`ID` int, `PHONE` int, `description` varchar(4), `DATE` varchar(11))
;
INSERT INTO table1
(`ID`, `PHONE`, `description`, `DATE`)
VALUES
(1, 600898367, 'main', '2016-01-23'),
(2, 600898367, NULL, '2016-01-24'),
(3, 600898367, NULL, '2016-01-26'),
(4, 600898367, NULL, '2016-01-28'),
(5, 662349093, 'main', '2016-01-10'),
(6, 662349093, NULL, '2016-01-21'),
(7, 662349093, NULL, '2016-01-30'),
(8, 662349093, NULL, '2016-01-31')
;
Query 1:
select t.*
from table1 t
inner join (
select `PHONE`, max(`DATE`) as `DATE` from table1 group by `PHONE`
) t1 on t.`PHONE` = t1.`PHONE` and (t.`DATE` = t1.`DATE` or t.`description` = 'main')
order by t.`ID`
Results:
| ID | PHONE | description | DATE |
|----|-----------|-------------|------------|
| 1 | 600898367 | main | 2016-01-23 |
| 4 | 600898367 | (null) | 2016-01-28 |
| 5 | 662349093 | main | 2016-01-10 |
| 8 | 662349093 | (null) | 2016-01-31 |
You can use the following query:
SELECT t1.*, t3.*
FROM mytable AS t1
LEFT JOIN (
SELECT PHONE, MAX(date) AS max_date
FROM mytable
GROUP BY PHONE
) AS t2 ON t1.PHONE = t2.PHONE
LEFT JOIN mytable AS t3 ON t1.PHONE = t3.PHONE AND t2.max_date = t3.`date`
WHERE t1.description = 'main'
With a GROUP BY we'll first group on both PHONE and description, thus getting 4 rows.
Next we'll create a comma separated set using GROUP_CONCAT. It can take an ORDER BY clause, to order the phone numbers by date.
Last we want to get the first item from the set, we can do this with SUBSTRING_INDEX.
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(`PHONE` ORDER BY `DATE`), ',', 1) AS `PHONE`,
description
FROM table1
GROUP BY `PHONE`, `description`;
See Fiddle
Try the following query..
SELECT
A.*
FROM
`old_young` A
INNER JOIN
(SELECT
MIN(`DATE`) AS Res
FROM
old_young
WHERE description = 'main'
GROUP BY PHONE
UNION
ALL
SELECT
MAX(`DATE`) AS Res
FROM
old_young
WHERE description = ''
GROUP BY PHONE) B
ON A.DATE = B.Res ;
Check the FIDDLE

select previous row based on group in MYSQL

I have this data.
create table #temp_student_ticket
(
student_ticket_id int identity(1,1) primary key,
student_id int ,
ticket_amount numeric(9,2)
)
insert into #temp_student_ticket
(
student_id,
ticket_amount
)
values
(
1,30.00
),(2,180.00),(1,75.00),(2,66.00)
select * from #temp_student_ticket
I need to select the previous row for the same student. Output should look like below.
student_id student_ticket_id amount prev_student_ticket_id prev_amount
1 1 20.00 NULL NULL
1 3 75.00 1 20.00
1 5 30.00 3 75.00
2 2 180.00 NULL NULL
2 4 66.00 2 180.00
What is the most optimized way to select this format on 50 million rows of table in MYSQL.
Thanks in advance.
Try this:
SELECT tst1.student_id, tst1.student_ticket_id, tst1.ticket_amount,
tst2.student_ticket_id prev_student_ticket_id, tst2.ticket_amount previous_amount
FROM temp_student_ticket tst1
LEFT JOIN temp_student_ticket tst2
ON (tst1.student_id = tst2.student_id
AND tst1.student_ticket_id > tst2.student_ticket_id
AND tst2.student_ticket_id = (SELECT MAX(tst3.student_ticket_id)
FROM temp_student_ticket tst3
WHERE tst3.student_id = tst1.student_id
AND tst3.student_ticket_id < tst1.student_ticket_id));