SQL Pivot Grid (Rows to Column - sql

Hi im new to pivot tables and i hope you can help me out. I have a table called Additional User Info... It would be easier to show rather than explain:
Source:
T_Info
(ID No, Field, Value)
1000, Gender, Male
1000, Age, 18
1000, School, MIT
Result That I Want
ID, Gender, Age, School
1000, Male, 18, MIT
is this possible without using a cursor?

SELECT IDNo,
MAX(CASE WHEN field = 'Gender' THEN Value END) Gender,
MAX(CASE WHEN field = 'Age' THEN Value END) Age,
MAX(CASE WHEN field = 'School' THEN Value END) School
FROM mytable
GROUP BY IDNo

Related

Split one column into 2 columns based on the last character of the column

I have a column region_no in a table. The column can hold 2 kinds of values - one set of values that end with the letter 'R' and the other that end with letter 'B'. This column must be split into 2 columns based on the last letter.
The create table and insert of sample data is :
CREATE TABLE test_17Jan
(
Region_No varchar(8),
Customer_Name varchar(20),
City varchar(20),
zip_code varchar(10)
)
INSERT INTO test_17Jan VALUES ('101R', 'John Doe', 'Detroit', '48127')
INSERT INTO test_17Jan VALUES ('202B', 'John Doe', 'Detroit', '48127')
INSERT INTO test_17Jan VALUES ('201B', 'Tim Smith', 'Waunakee', '53597')
The desired output is :
Customer_Name
City
zip_code
Inside_Sales_Region
B2B_Region
John Doe
Detroit
48127
101R
202B
Tim Smith
Waunakee
53597
NULL
201B
I thought of pivot function, but that needs to have an aggregate. Is there a way to get the output in the above format? Any help will be appreciated. The code will run on SQL Server 2019 (v15).
You can use conditional aggregation to get your desired results:
select Customer_Name, City, zip_code,
Max(Inside_Sales_Region) Inside_Sales_Region,
Max(B2B_Region) B2B_Region
from (
select Customer_Name, City, zip_code,
case when Right(Region_No,1) = 'R' then Region_No end Inside_Sales_Region,
case when Right(Region_No,1) = 'B' then Region_No end B2B_Region
from test_17Jan
)t
group by Customer_Name, City, zip_code;
better option should be LEFT SELF JOIN. But this should also work.
SELECT
CUSTOMER_NAME, CITY, ZIP_CODE,
CASE
WHEN Region_No like '%R' THEN Region_No
ELSE NULL AS Inside_Sales_Region,
CASE
WHEN Region_No like '%B' THEN Region_No
ELSE NULL AS B2B_Region
FROM test_17Jan;

SELECT query with combined conditions

SQL noobie here, tried my luck googling around, but came up empty or didn't know the proper keyword. Still, feeling quite awkward between all those advanced questions, however still hopeful to get a solution and learn.
Let's suppose we have a table representing participants for different teams for a children sports tournament.
Participant table:
Our goal is to select out participants that have chosen a WRONG team. Let's suppose that the conditions for the teams are as such:
team Yellow = boys with age 12;
team Red = girls with age 13;
team Blue = boys with age 11;
That would mean that the incorrect registrants are Sarah (incorrect gender, correct age), Jack (incorrect gender and age) and Mary who all should therefore be included in the result of the query.
However I'm struggling with creating a SQL query that would consider conditions from multiple fields (comparing team towards gender and age at the same time) + having more than one set of comparison done at the same time (looking for incorrect participants from 3 teams at the same time).
Help is much appreciated!
You didn't state your DBMS so this is ANSI SQL:
Just select all rows that do not comply with any of the rules:
select *
from participants
where (team, gender, age) not in ( ('Yellow', 'M', 12),
('Red', 'F', 13),
('Blue', 'M', 11) );
Online example: http://rextester.com/ZTEON26060
Main thing here is you have to convert your team rules into some kind of proper data structure. You can put it into the table, or use derived table, like this:
select *
from participants as p
where
not exists (
select *
from (values
('Yellow', 'M', 12),
('Red', 'F', 13),
('Blue', 'M', 11)
) as t(Team, Gender, Age)
where
t.Team = p.Team and
t.Gender = p.Gender and
t.Age = p.Age
)
Or you can check for correct team and then compare with current team:
select
p.*, t.Team as Correct_Team
from participants as p
left join (values
('Yellow', 'M', 12),
('Red', 'F', 13),
('Blue', 'M', 11)
) as t(Team, Gender, Age) on
t.Gender = p.Gender and
t.Age = p.Age
sql fiddle demo
You can try this :
Select
Name,
Gender,
Age,
Team AS Chosen_team,
Case when Gender='M' and Age=12 Then 'Yellow'
when Gender='F' and Age=13 then 'Red'
when gender='M' and Age=11 then 'Blue'
End as Ideal_team,
Case when Chosen_team <> Ideal_team then 'FALSE' ELSE 'TRUE'
from your_table;
Now select the records with value false. You will get your list.
You can use a combination of or and and to do this.
select * from yourtable
where (team ='Yellow' and not (gender = 'M' and age = 12))
or (team ='Red' and not (gender = 'F' and age = 13))
or (team ='Blue' and not (gender = 'M' and age = 11))
There are a few things you haven't mentioned about your team restrictions:
Can you have different combinations of Age and Gender for the same teams?
Can any of the same Age and Gender combinations match multiple teams?
Would the valid teams cover all permutations of of participant ages and genders?
Since that's not stated, I'm going to provide a generic solution that makes it easy to extend the valid teams. And the query will return all participants that don't match at least one valid team. (NOTE: It may be that there is no valid team for a particular participant.)
Approach:
Put valid combinations in a temporary (or even persistent) table of some sorts (I'll use a CTE).
Select all participants where you cannot find a matching Age, Gender and Team in the "Valid Teams" table.
The CTE for ValidTeams below could easy be replaced with a table if your RDBMS doesn't support CTE's. NOTE: If there are many permutations of valid gender/age/team a separate table will be better.
;WITH ValidTeams AS (
SELECT 'Yellow' AS Team, 'M' AS Gender, 12 AS Age
UNION ALL SELECT 'Red', 'F', 13
UNION ALL SELECT 'Blue', 'M', 11
)
SELECT Name, Gender, Age, Team AS InvalidTeam
FROM Participants p
WHERE NOT EXISTS (
SELECT *
FROM ValidTeams v
WHERE v.Gender = p.Gender
AND v.Age = p.Age
AND v.Team = p.Team
)

group by + counting different column values

I have a simple table with id, gender, age and favoriteMovie.
Gender column has values male and female only.
I want to display all movie titles from movies column (group by movies) and in two separated columns number of males and number of females voting on selected movie.
Something like count(gender = 'male') as male, count(gender = 'female') as female
Database is ms-sql 2008
Any sugestion greatly appreciated
You can use case syntax (while change count into sum):
select favoriteMovie as movie,
sum(case
when gender = 'male' then
1
else
0
end) as male,
sum(case
when gender = 'female' then
1
else
0
end) as female
from MyTable
group by favoriteMovie
Not very good with pivots, but this worked for me.
declare #t table (id varchar(55),gender varchar(55),age varchar(55),favoriteMovie varchar(55))
insert into #t values(1,'m',16,'star wars')
insert into #t values(2,'f',16,'star trek')
insert into #t values(3,'m',16,'star trek')
insert into #t values(4,'f',16,'star wars')
insert into #t values(5,'m',16,'star wars')
And then query.
select favoriteMovie, sum([m]) as "m",sum([f]) as "f"
from #t
pivot
(
count(gender)
for gender in ([m],[f])
) as pt
group by favoriteMovie

SQL query to separate a column into separate columns

I would like to have separate columns for H and T's prices, with 'period' as the common index. Any suggestions as to how I should go about this?
This is what my SQL query produces at the moment:
You can use GROUP BY and a conditional, like this:
SELECT
period
, SUM(CASE NAME WHEN 'H' THEN price ELSE 0 END) as HPrice
, SUM(CASE NAME WHEN 'T' THEN price ELSE 0 END) as TPrice
FROM MyTable
GROUP BY period
You can do the following:
SELECT period,
max(CASE WHEN name = 'H' THEN price END) as h_price,
max(CASE WHEN name = 'T' THEN price END) as t_price
FROM myTable
GROUP by period
If you mean to recreate the table?
1) Create a new table with columns: period, price_h & price_t.
2) Copy all (distinct) from period into new table's period.
3) Copy all price where name = H to new table's price_h joining the period column
4) repeat 3 for price_t....
good luck!
A little late to the game on this but you could also pivot the data.
Lets create a sample table.
CREATE TABLE myData(period int, price decimal(12,4), name varchar(10))
GO
-- Inserting Data into Table
INSERT INTO myData
(period, price, name)
VALUES
(1, 53.0450, 'H'),
(1, 55.7445, 'T'),
(2, 61.2827, 'H'),
(2, 66.0544, 'T'),
(3, 61.3405, 'H'),
(3, 66.0327, 'T');
Now the select with the pivot performed.
SELECT period, H, T
FROM (
SELECT period, price, name
FROM myData) d
PIVOT (SUM(price) FOR name IN (H, T)) AS pvt
ORDER BY period
I've used this technique when I needed to build a dynamic sql script that took in the columns in which would be displayed on the header of the table. No need for case statements.
Im not sure about the performance of the case and pivot. Maybe someone with a little more experience could add some comments on which would give better performance.

SQL - How to display the students with the same age?

the code I wrote only tells me how many students have the same age. I want their names too...
SELECT YEAR(CURRENT DATE-DATEOFBIRTH) AS AGE, COUNT(*) AS HOWMANY
FROM STUDENTS
GROUP BY YEAR(CURRENT DATE-DATEOFBIRTH);
this returns something like this:
AGE HOWMANY
--- -------
21 3
30 5
Thank you.
TABLE STUDENTS COLUMNS:
StudentID (primary key), Name(varchar), Firstname(varchar), Dateofbirth(varchar)
I was thinking of maybe using the code above and somewhere add the function concat that will put the stundents' names on the same row as in
your existing SQL looks like it has errors, but you could use GROUP_CONCAT:
add GROUP_CONTACT(colname) as another column to fetch, then split by , in your application
The resulting data set does not appear useful on the surface based on the question unless you are looking for a listing of students, their age, and how many other students are of the same age:
SELECT NAME, AGE, HOWMANY
FROM STUDENTS AS S,
(SELECT YEAR(CURRENT DATE-DATEOFBIRTH) AS AGE,
COUNT(*) AS HOWMANY
FROM STUDENTS
GROUP BY YEAR(CURRENT DATE-DATEOFBIRTH)
) AS A
WHERE YEAR(CURRENT DATE-S.DATEOFBIRTH) = A.AGE
Basically perform a self-join with the age counts you have calculated.
What about...
SELECT name FROM students WHERE age = ENTER_AGE_HERE;
You have the names and the number of students can be found by finding the number of entries you get from the query.
For example, in PHP, you can find the length of the array.
Of course, you have to change to names in my example to the names used in your database.
CREATE TABLE #Student
(
id int identity(1,1),
age int,
name varchar(255)
)
INSERT INTO #Student S
VALUES(21,'bob'),
(21,'tom'),
(21,'dick'),
(21,'william'),
(35,'mark'),
(35,'anthony')
SELECT age,COUNT(*),STUFF(
(
SELECT ',' + name
FROM #Student SS
WHERE SS.age = S.age
FOR XML PATH('')
), 1, 1, '')
FROM #Student s
GROUP BY age