Will a PIVOT meet my requirements? - sql

Please see the DDL below:
CREATE TABLE Person (PersonNo int, Name varchar, Age int,address varchar(100))
Is it possible to SELECT the data in the following format using a single query:
PersonNo Name
PersonNo Age
PersonNo Address
I would need a column to identify the type of row e.g. Name. The more I think about this the more I don't think I can do it. I am currently trying to do it with a pivot.

One option would be to use UNION ALL (just remember to convert the int to varchar):
select personno, name as value, 'Name' as type
from person
union all
select personno, CONVERT(varchar(10), age), 'Age' as type
from person
union all
select personno, address, 'Address' as type
from person
SQL Fiddle Demo
Also please note -- you should define a length for the Name field. As is, it can only contain a single character.

You are looking for an UNPIVOT, not a PIVOT
SELECT
PersonNo
,ColumnName
,ColumnValue
FROM Person t1
UNPIVOT(ColumnValue FOR ColumnName in ([Name],[Age],[Address])) t2

Related

How to execute a select with a WHERE using a not-always-existing column

Simple example: I have some (nearly) identical tables with personal data (age, name, weight, ...)
Now I have a simple, but long SELECT to find missing data:
Select ID
from personal_data_a
where
born is null
or age < 1
or weight > 500
or (name is 'John' and surname is 'Doe')
Now the problem is:
I have some personal_data tables where the column "surname" does not exit, but I want to use the same SQL-statement for all of them. So I have to check (inside the WHERE clause) that the last OR-condition is only used "IF the column surname exists".
Can it be done in a simple way?
You should have all people in the same table.
If you can't do that for some reason, consider creating a view. Something like this:
CREATE OR REPLACE VIEW v_personal_data
AS
SELECT id,
born,
name,
surname,
age,
weight
FROM personal_data_a
UNION ALL
SELECT id,
born,
name,
NULL AS surname, --> this table doesn't contain surname
age,
weight
FROM personal_data_b;
and then
SELECT id
FROM v_personal_data
WHERE born IS NULL
OR age < 1
OR ( name = 'John'
AND ( surname = 'Doe'
OR surname IS NULL))
Can it be done in a simple way?
No, SQL statements work with static columns and the statements will raise an exception if you try to refer to a column that does not exist.
You will either:
need to have a different query for tables with the surname column and those without;
have to check in the data dictionary whether the table has the column or not and then use dynamic SQL to build your query; or
to build a VIEW of the tables which do not have that column and add the column to the view (or add a GENERATED surname column with a NULL value to the tables that are missing it) and use that instead.
While dynamic predicates are usually best handled by the application or by custom PL/SQL objects that use dynamic SQL, you can solve this problem with a single SQL statement using DBMS_XMLGEN, XMLTABLE, and the data dictionary. The following code is not what I would call "simple", but it is simple in the sense that it does not require any schema changes.
--Get the ID column from a PERSONAL table.
--
--#4: Get the IDs from the XMLType.
select id
from
(
--#3: Convert the XML to an XMLType.
select xmltype(personal_xml) personal_xmltype
from
(
--#2: Convert the SQL to XML.
select dbms_xmlgen.getxml(v_sql) personal_xml
from
(
--#1: Use data dictionary to create SQL statement that may or may not include
-- the surname predicate.
select max(replace(replace(
q'[
Select ID
from #TABLE_NAME#
where
born is null
or age < 1
or weight > 500
or (name = 'John' #OPTIONAL_SURNAME_PREDICATE#)
]'
, '#TABLE_NAME#', table_name)
, '#OPTIONAL_SURNAME_PREDICATE#', case when column_name = 'SURNAME' then
'and surname = ''Doe''' else null end)) v_sql
from all_tab_columns
--Change this literal to the desired table.
where table_name = 'PERSONAL_DATA_A'
)
)
where personal_xml is not null
)
cross join xmltable
(
'/ROWSET/ROW'
passing personal_xmltype
columns
id number path 'ID'
);
See this db<>fiddle for a runnable example.

Character Format in SQL

I am new to SQL code writing. How do I format EmployeeID as character(9)? It is currently shown as an int. I need it as character(9) since I will be joining that field up to a different table.
SELECT ,UserID
,EmployeeID
from DASH.tblUserHierarchy
SELECT UserID,
CAST(EmployeeID AS VARCHAR(9)) EmployeeID
FROM DASH.tblUserHierarchy
This should work

sql concat within a SELECT statement

This is similiar to this one. How to concatenate all columns in a select with SQL Server
But not quite. This is MS SQL 2008. I am pulling patient demographics, and one of those is race, which is a multi-choice field (you could be asian and chinese for example). The race table 'PatientRace' is linked to the patient table by patientid. So the table structure is thus:
Patient table
PatientID
PatientName
PatientAddress
PatientRace table
PatientRaceID
PatientID (FK)
Description
I only want one row, and I want race to be concatenated. Is it possible to do this within a single SELECT statement or do I need to do a cursor? I am envisioning the cursor to be like this: Initial select for all the other demographics, insert into a temp table. Go through the temp table by patientID, then for each, grab out the race, concat, and add to the temp table.
The desired output is like this: 1 row per patient.
Name: "Joe Blow"
Race: "Asian, Chinese"
You need to use STUFF and FOR XML like this
SELECT p.PatientName,
(STUFF(SELECT ',' + r.Description
FROM PatientRace r
WHERE r.PatientID = p.PatientID
FOR XML('')), 1, 1, '')
FROM Patients p
Concatenating string values in SQL Server is not obvious. It requires using "xml" data processing and a subquery:
select p.*,
stuff((select ', ' + Description
from patientrace pr
where pr.patientid = p.patientid
for xml path ('')
), 1, 2, ''
) as races
from patients p;
As for me, you have write function like fn_GetRaceByID(int PatientID), that returns desired string. So use it in your select. Link in your question has good example, how to do this.

how to transform vertical fields in a table to horizontal result by SQL

I have a table like this:
create table t1 {
person_id int,
item_name varchar(30),
item_value varchar(100)
};
Suppose person_id+item_name is the composite key, now I have some data (5 records) in table t1 as below:
person_id ====item_name ====== item_value
1 'NAME' 'john'
1 'GENDER' 'M'
1 'DOB' '1970/02/01'
1 'M_PHONE' '1234567890'
1 'ADDRESS' 'Some Addresses unknown'
Now I want to use SQL (or combing store procedure/function or whatever) to query the above result (1 result set) become:
NAME==GENDER==DOB========M_PHONE=======ADDRESS===============
1 M 1970/02/01 1234567890 Some Addresses unknown
How should I do ?
Thank you for your help.
Regardless of the database you are using, the concept of what you are trying to achieve is called "Pivot Table".
Here's an example for mysql:
http://en.wikibooks.org/wiki/MySQL/Pivot_table
Some databases have builtin features for that, see the links below.
SQLServer:
http://msdn.microsoft.com/de-de/library/ms177410.aspx
Oracle:
http://www.dba-oracle.com/t_pivot_examples.htm
You can always create a pivot by hand. Just select all the aggregations in a result set and then select from that result set. Note, in your case, you can put all the names into one column using concat (i think that's group_concat in mysql), since you cannot know how many names are related to a person_id.
Finally, I found the solution in PostgreSQL:
select * from crosstab ('select person_id, item_name, item_value from t1 where person_id = 1 ')
as virtual_table ( person_id integer, name varchar, gender varchar, dob varchar, m_phone varchar, address varchar)
Also need to install the crosstab function on Postgres. See more: http://www.postgresql.org/docs/8.3/static/tablefunc.html

Dynamic Columns in

I have table with the following static columns
ID Sys_Date Name prop_name1 prop_name2 prop_name3 prop_value1 prop_value2 prop_value3
10 11/2/2011Java class method parameter Imanager getOrders orderNumber
I need to write SQL query which get an input property name like “method” and go over (prop_name1 prop_name2 prop_name3 ) and check which column
Is equal to “method” in case I found it I need to jump 3 columns to the proper value which is “getOrders” and get the value from there where
prop_name1 is mapping to prop_value1
prop_name2 is mapping to prop_value2
prop_name3 is mapping to prop_value3
how can I do it with sql query?
Thanks in advance
You could do something like this:
select name,value
from
(
select id, prop_name name, prop_name value
from table
union
select id, prop_name2 name, prop_name2 value
from table
union
select id, prop_name3 name, prop_name3 value
from table
)
where name = 'method'
...which is basically shoe-horning your data into a more easily queryable structure. You'd be better off changing the table structure, though..