SQL displaying results as columns from rows - sql

SQL Noob here.
I realize that many variations to this question have been asked but none seems to work or be fully applicable to my annoying situation, ie. I dont think PIVOT would work for what I require. I cant fathom the necessary words to google what I need efficiently.
I have the following query:
Select w.WORKORDERID, y.Answer
From
[SD].[dbo].[WORKORDERSTATES] w
LEFT JOIN [SD].[dbo].[WO_RESOURCES] x
ON w.workorderid = x.woid
Full Outer Join [SD].[dbo].ResourcesQAMapping y
ON x.UID = y.MAPPINGID
WHERE w.APPR_STATUSID = '2'
AND w.STATUSID = '1'
AND w.REOPENED = 'false'
It will bring back the following result:
+-----------+---------------------+
| WORKORDER | Answer |
+-----------+---------------------+
| 55693 | Brad Pitt |
| 55693 | brad.pitt#mycom.com |
| 55693 | Location |
| 55693 | NULL |
| 55693 | george |
+-----------+---------------------+
I would like all rows related to the value 55693 to output as columns like below:
+-----------+-----------+---------------------+----------+--------+--------+
| WORKORDER | VALUE1 | VALUE2 | VALUE3 | VALUE4 | VALUE5 |
+-----------+-----------+---------------------+----------+--------+--------+
| 55693 | Brad Pitt | brad.pitt#mycom.com | Location | NULL | george |
+-----------+-----------+---------------------+----------+--------+--------+
There will always be the same amount of values, and I am almost sure that the solution involves creating a temporary table but I cant get it to work any which way.
Any help would be greatly appreciated.

If you always have the same number of values (5) you can use a static PIVOT, otherwise you need a dynamic TSQL statement with PIVOT.
In both cases you'll need to add a column to guarantee rows/columns ordering otherwise there is no guarantee that you'll see the correct value in each column.
Here is a sample query thet uses a static PIVOT on 5 values (but remember to add a column to properly order the data replacing ORDER BY WORKORDER with ORDER BY YOUR_COLUMN_NAME):
declare #tmp table (WORKORDER int, Answer varchar(50))
insert into #tmp values
(55693, 'Brad Pitt')
,(55693, 'brad.pitt#mycom.com')
,(55693, 'Location')
,(55693, 'NULL')
,(55693, 'george')
select * from
(
select
WORKORDER,
Answer,
CONCAT('VALUE', ROW_NUMBER() OVER (PARTITION BY WORKORDER ORDER BY WORKORDER)) AS COL
from #tmp
) as src
pivot
(
max(Answer) for COL in ([VALUE1], [VALUE2], [VALUE3], [VALUE4], [VALUE5])
)
as pvt
Results:

Try to select another column that has different values as answer column and try to run pivot and that will work

Related

HQL, insert two rows if a condition is met

I have the following table called table_persons in Hive:
+--------+------+------------+
| people | type | date |
+--------+------+------------+
| lisa | bot | 19-04-2022 |
| wayne | per | 19-04-2022 |
+--------+------+------------+
If type is "bot", I have to add two rows in the table d1_info else if type is "per" i only have to add one row so the result is the following:
+---------+------+------------+
| db_type | info | date |
+---------+------+------------+
| x_bot | x | 19-04-2022 |
| x_bnt | x | 19-04-2022 |
| x_per | b | 19-04-2022 |
+---------+------+------------+
How can I add two rows if this condition is met?
with a Case When maybe?
You may try using a union to merge or duplicate the rows with bot. The following eg unions the first query which selects all records and the second query selects only those with bot.
Edit
In response to the edited question, I have added an additional parity column (storing 1 or 0) named original to differentiate the duplicate entry named
SELECT
p1.*,
1 as original
FROM
table_persons p1
UNION ALL
SELECT
p1.*,
0 as original
FROM
table_persons p1
WHERE p1.type='bot'
You may then insert this into your other table d1_info using the above query as a subquery or CTE with the desired transformations CASE expressions eg
INSERT INTO d1_info
(`db_type`, `info`, `date`)
WITH merged_data AS (
SELECT
p1.*,
1 as original
FROM
table_persons p1
UNION ALL
SELECT
p1.*,
0 as original
FROM
table_persons p1
WHERE p1.type='bot'
)
SELECT
CONCAT('x_',CASE
WHEN m1.type='per' THEN m1.type
WHEN m1.original=1 AND m1.type='bot' THEN m1.type
ELSE 'bnt'
END) as db_type,
CASE
WHEN m1.type='per' THEN 'b'
ELSE 'x'
END as info,
m1.date
FROM
merged_data m1
ORDER BY m1.people,m1.date;
See working demo db fiddle here
I think what you want is to create a new table that captures your logic. This would simplify your query and make it so you could easily add new types without having to edit logic of a case statement. It may also make it cleaner to view your logic later.
CREATE TABLE table_persons (
`people` VARCHAR(5),
`type` VARCHAR(3),
`date` VARCHAR(10)
);
INSERT INTO table_persons
VALUES
('lisa', 'bot', '19-04-2022'),
('wayne', 'per', '19-04-2022');
CREATE TABLE info (
`type` VARCHAR(5),
`db_type` VARCHAR(5),
`info` VARCHAR(1)
);
insert into info
values
('bot', 'x_bot', 'x'),
('bot', 'x_bnt', 'x'),
('per','x_per','b');
and then you can easily do a join:
select
info.db_type,
info.info,
persons.date date
from
table_persons persons inner join info
on
info.type = persons.type

Using dynamic unpivot with columns with different types

i have a table with around 100 columns named F1, F2, ... F100.
I want to query the data row-wise, like this:
F1: someVal1
F2: someVal2
...
I am doing all this inside a SP, therefore, I am generating the sql dynamically.
I have successfully generated the following sql:
select CAST(valname as nvarchar(max)), CAST(valvalue as nvarchar(max)) from tbl_name unpivot
(
valvalue for valname in ([form_id], [F1],[F2],[F3],[F4],[F5],[F6],[F7],[F8],[F9],[F10],[F11],[F12],[F13],[F14],[F15],[F16],[F17],[F18],[F19],[F20],[F21],[F22],[F23],[F24],[F25],[F26],[F27],[F28],[F29],[F30],[F31],[F32],[F33],[F34],[F35],[F36],[F37],[F38],[F39],[F40],[F41],[F42],[F43],[F44],[F45],[F46],[F47],[F48],[F49],[F50],[F51],[F52],[F53],[F54],[F55],[F56],[F57],[F58],[F59],[F60],[F61],[F62],[F63],[F64],[F65],[F66],[F67],[F68],[F69],[F70],[F71],[F72],[F73],[F74],[F75],[F76],[F77],[F78],[F79],[F80],[F81],[F82],[F83],[F84],[F85])
) u
But on executing this query, I get this exception:
The type of column "F3" conflicts with the type of other columns
specified in the UNPIVOT list.
I guess this is because F3 is varchar(100) while form_id, F1 and F2 are varchar(50). According to my understanding, I shouldn't be getting this error because I am casting all the results to nvarchar(max) in the select statement.
This table has all kinds of columns like datetime, smallint and int.
Also, all the columns of this table except one have SQL_Latin1_General_CP1_CI_AS collaltion
What is the fix for this error ?
this solution is you must use a subquery to let all columns be the same type to have the same length.
Try to CAST the values in subquery then unpivot instead of select
select valname, valvalue
from (
SELECT
CAST([form_id] as nvarchar(max)) form_id,
CAST([F1] as nvarchar(max)) F1,
CAST([F2] as nvarchar(max)) F2,
CAST([F3] as nvarchar(max)) F3,
CAST([F4] as nvarchar(max)) F4,
....
FROM tbl_name
) t1 unpivot
(
valvalue for valname in ([form_id], [F1],[F2],[F3],[F4],[F5],[F6],[F7],[F8],[F9],[F10],[F11],[F12],[F13],[F14],[F15],[F16],[F17],[F18],[F19],[F20],[F21],[F22],[F23],[F24],[F25],[F26],[F27],[F28],[F29],[F30],[F31],[F32],[F33],[F34],[F35],[F36],[F37],[F38],[F39],[F40],[F41],[F42],[F43],[F44],[F45],[F46],[F47],[F48],[F49],[F50],[F51],[F52],[F53],[F54],[F55],[F56],[F57],[F58],[F59],[F60],[F61],[F62],[F63],[F64],[F65],[F66],[F67],[F68],[F69],[F70],[F71],[F72],[F73],[F74],[F75],[F76],[F77],[F78],[F79],[F80],[F81],[F82],[F83],[F84],[F85])
) u
In a simplest way I would use CROSS APPLY with VALUES to do unpivot
SELECT *
FROM People CROSS APPLY (VALUES
(CAST([form_id] as nvarchar(max))),
(CAST([F1] as nvarchar(max))),
(CAST([F2] as nvarchar(max))),
(CAST([F3] as nvarchar(max))),
(CAST([F4] as nvarchar(max))),
....
) v (valvalue)
Here is a sample about CROSS APPLY with VALUES to do unpivot
we can see there are many different types in the People table.
we can try to use cast to varchar(max), let columns be the same type.
CREATE TABLE People
(
IntVal int,
StringVal varchar(50),
DateVal date
)
INSERT INTO People VALUES (1, 'Jim', '2017-01-01');
INSERT INTO People VALUES (2, 'Jane', '2017-01-02');
INSERT INTO People VALUES (3, 'Bob', '2017-01-03');
Query 1:
SELECT *
FROM People CROSS APPLY (VALUES
(CAST(IntVal AS VARCHAR(MAX))),
(CAST(StringVal AS VARCHAR(MAX))),
(CAST(DateVal AS VARCHAR(MAX)))
) v (valvalue)
Results:
| IntVal | StringVal | DateVal | valvalue |
|--------|-----------|------------|------------|
| 1 | Jim | 2017-01-01 | 1 |
| 1 | Jim | 2017-01-01 | Jim |
| 1 | Jim | 2017-01-01 | 2017-01-01 |
| 2 | Jane | 2017-01-02 | 2 |
| 2 | Jane | 2017-01-02 | Jane |
| 2 | Jane | 2017-01-02 | 2017-01-02 |
| 3 | Bob | 2017-01-03 | 3 |
| 3 | Bob | 2017-01-03 | Bob |
| 3 | Bob | 2017-01-03 | 2017-01-03 |
Note
when you use unpivot need to make sure the unpivot columns date type are the same.
Many ways a cat can skin you, or vice-versa.
Jokes apart, what D-Shih suggested is what you should start with and may get you home and dry.
In a majority of cases;
Essentially the UNPIVOT operation is concatenating the data from multiple rows. Starting with a CAST operation is the best way forward as it makes the data types identical(preferably a string type like varchar or nvarchar), its also a good idea to go with the same length for all UNPIVOTED columns in addition to having the same type.
In other cases;
If this still does not solve the problem, then you need to look deeper and check whether the ANSI_Padding setting is ON or OFF across all columns on the table. In latter day versions of SQL server this is mostly ON by default, but some developers may customise certain columns to have ANSI_PADDING set to off. If you have a mixed setup like this its best to move the data to another table with ANSI_PADDING set to ON. Try using the same UNPIVOT query on that table and it should work.
Check ANSI_Padding Status
SELECT name
,CASE is_ansi_padded
WHEN 1 THEN 'ANSI_Padding_On'
ELSE 'ANSI_Padding_Off'
AS [ANSI_Padding_Check]
FROM sys.all_columns
WHERE object_id = object_id('yourschema.yourtable')
Many situations be better suited for CROSS APPLY VALUES. It all depends on you, the jockey to choose horses for courses.
Cheers.

SQL Server stored procedure inserting duplicate rows

I have a table with column GetDup and I'd like to the duplicate records based on the value of this column. For example, if value on is 1 in GetDup, then duplicate the record once. If value in the column is 2, then duplicate the record twice and so on and the statement has to be in looping statement.
What will be a good way to write a stored procedures for this? Please help.
Input:
+--------+--------------+---------------+
| Getdup | CustomerName | CustomerAdd |
+--------+--------------+---------------+
| 1 | John | 123 SomeWhere |
| 2 | Bob | 987 SomeWhere |
+--------+--------------+---------------+
What I want:
+--------+--------------+---------------+
| Getdup | CustomerName | CustomerAdd |
+--------+--------------+---------------+
| 1 | John | 123 SomeWhere |
| 1 | John | 123 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
+--------+--------------+---------------+
picture of data
Answer #2 After Clarification
Number Table to the Rescue!
The number table in my example (or tally table, if you want to call it that), is both temporary and very small. To make it bigger, just add more values to z and add more CROSS JOINs. In my opinion, a number table and a calendar table are both things that should be in every database you have. They are extremely useful.
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE mytable ( Getdup int, CustomerName varchar(10), CustomerAdd varchar(20) ) ;
INSERT INTO mytable (Getdup, CustomerName, CustomerAdd)
VALUES (1,'John','123 SomeWhere'), (2,'Bob','987 SomeWhere')
;
Query 1:
;WITH z AS (
SELECT *
FROM ( VALUES(0),(0),(0),(0) ) v(x)
)
, numTable AS (
SELECT num
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY z1.x)-1 num
FROM z z1
CROSS JOIN z z2
) s1
)
SELECT t1.Getdup, t1.CustomerName, t1.CustomerAdd
FROM mytable t1
INNER JOIN numTable ON t1.getdup >= numTable.num
ORDER BY CustomerName, CustomerAdd
Results:
| Getdup | CustomerName | CustomerAdd |
|--------|--------------|---------------|
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 2 | Bob | 987 SomeWhere |
| 1 | John | 123 SomeWhere |
| 1 | John | 123 SomeWhere |
--------------------------------------------------------------------------
ORIGINAL ANSWER
EDIT: After further clarification of the problem, this won't duplicate rows, this will only duplicate the data in a column.
Something like one of these might work.
T-SQL
SELECT replicate(mycolumn,getdup) AS x
FROM mytable
MySQL
SELECT repeat(mycolumn,getdup) AS x
FROM mytable
Oracle SQL
SELECT rpad(mycolumn,getdup*length(mycolumn),mycolumn) AS x
FROM mytable
PostgreSQL
SELECT repeat(mycolumn,getdup+1) AS x
FROM mytable
If you can provide more details for exactly what you want and what you're working with, we might be able to help you better.
NOTE 2: Depending on what you need, you may need to do some math magic. You say above if GetDup is 1 then you want one duplicate. If that means that your output should be GetDup``GetDup, then you'll want to add one in the repeat(),replicate() or rpad() functions. ie replicate(mycolumn,getdup+1). Oracle SQL will be a little different, since it uses rpad().
In standard SQL you can use a recursive CTE:
with recursive cte as (
select t.dup, . . .
from t
union all
select cte.dup - 1, . . .
from cte
where cte.dup > 1
)
select *
from cte;
Of course, not all databases support recursive CTEs (and the recursive keyword is not used in some of them).
So, you want recursive solution :
with t as (
select Getdup, CustomerName, CustomerAdd, 0 as id
from table
union all
select Getdup, CustomerName, CustomerAdd, id + 1
from t
where id < getdup
)
insert into table (col1, col2, col3)
select Getdup, CustomerName, CustomerAdd
from t
order by getdup
option (maxrecursion 0);

get 2 column values to 2 columns for 1 row (or comma-separated)

I have a problem where I have to alter another programmer's very large SQL query.
My problem is described by this table:
+--------+-----------+---------------+-------------------------+
| Id | ProductId | Barcode | CreatedAt |
+--------+-----------+---------------+-------------------------+
| 30665 | 312118 | 4054065383840 | 2017-03-13 18:37:13.130 |
| 128600 | 312118 | 4054065383857 | 2017-05-22 13:26:48.683 |
+--------+-----------+---------------+-------------------------+
So as you can see, a product has 2 barcodes. In our query, I need to display these 2 barcodes in 2 columns, instead of 2 different rows, as barcode1 and barcode2, or some such.
SELECT ISNULL(pp.Barcode,'') AS BarCode
FROM (...) c
LEFT JOIN ProductBarcode pp on pp.ProductId=c.VariantProductId
This is the query in use atm.
Hopefully I explained it well enough.
Thanks in advance,
Rasmus.
EDIT: db is MSSQL
Use pivot query , much effective in this scenario. Use more row numbers if there are more than 2 barcodes as barcode1,barcode2,barcode3
SELECT productid
,[1] AS Barcode1
,[2] AS Barcode2
FROM (
SELECT productid
,barcode
,ROW_NUMBER() OVER (
PARTITION BY productid ORDER BY barcode
) rn
FROM #mytable
) my
pivot(max(barcode) FOR rn IN ([1], [2])) AS pvt

Teradata SQL - select distinct returning duplicate rows where one row has null values

I'm stuck on query where i'm joining multiple tables to bring in attributes I need for a specific primary key. What I'm finding is I'm receiving duplicate rows, which are essentially the same, but one row has null (?) values in a few columns. I only want to return the row with populated data.
I've checked all of my vol tables up to this point and there are no duplicates and I have the same distinct row count until my final vol table. I am joining in new data from other tables in the final vol table, and will post that query, but just curious if anyone knows why this would happen with "SELECT DISTINCT".
I tried using a clause "WHERE PROD_LN IS NOT NULL", but I have some that are not duplicates and will not have values for PROD_LN. I was also thinking of trying a "CASE WHEN PROD_LN IS NULL THEN PROD_LN = PROD_LINE NOT NULL" but not sure if that would work. Any help is appreciated!
ACCT_NAME | GRP_ID | GRP_B | ASGND_CD | PROD_LN | PROD_TYP | PLCY_TYP | FINCL | MKT_SGMT |
ENTERPRISE A | 00012345 | N12345 | 1 | ? | ? | 8 | ? | ? |
ENTERPRISE A | 00012345 | N12345 | 1 | H | SPPO | 8 | ASO | AFG |
I think you want something like this:
select t.*
from t
qualify row_number() over (partition by ACCT_NAME, GRP_ID, GRP_B, ASGND_CD
order by prod_ln nulls last
) = 1;
I am guessing that by duplicate, you mean on the first four columns. In any case, the partition by should be the columns that you want to be unique.