SQL server convert one single row to column - sql-server

In SQL Server, how can I convert one single row of integer data, like this
ColumnName1 ColumnName2 ColumnName3
1 2 3
into a single row, order by DSEC?
ColumnNameTotalSort
3
2
1
I know the requirement seems simple but I have been struggling for a while.
Thanks for input.

As ZLK mentioned, UNPIVOT is an option
Another option is with a CROSS APPLY and VALUES
Example
Select B.*
From YourTable A
Cross Apply (values (ColumnName1)
,(ColumnName2)
,(ColumnName3)
) B(ColumnNameTotalSort)
-- Where Your Filter Condition Here
Order By ColumnNameTotalSort Desc
Returns
ColumnNameTotalSort
3
2
1

Related

tableau custom sql pivot

I have a SQL Server data source that I cannot change the structure of.
I have started pivoting the data with CustomSQL query below, but I need to modify the query so that when iterations 3, 4 ,5 ...n data are added to the source in the future, it automatically includes it in the pivoted data. I don't want to have to keep updating the query. Any ideas?
KPI Name Iteration 1 Iteration 2
a 1 2
b 50 51
Select [KPI]
, 'Iteration1' as [Iteration]
, [Iteration1] as [Count]
From [MC_KPI]
Union ALL
Select [KPI]
, 'Iteration2' as [Iteration]
, [Iteration2] as [Count]
From [KPI]
Now I have this
KPI Name Iteration 1 Iteration 2
a 1 1
a 2 2
b 1 50
b 2 51
What you are doing is called "unpivot" in SQL Server. You can see the description here:
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017
If you want to be able to add iterations without having to modify something in tableau, you can create a view in SQL that does unpivot and just do "select * from view" in tableau. This would give you the chance to change the view under the covers from tableau and have things continue to work (since the unpivot output is just a property bag and the columns are not really changing as you add properties into the output)

Field is being updated with same value

I have a table that has a new column, and updating the values that should go in the new column. For simplicity sake I am reducing my example table structure as well as my query. Below is how i want my output to look.
IDNumber NewColumn
1 1
2 1
3 1
4 2
5 2
WITH CTE_Split
AS
(
select
*,ntile(2) over (order by newid()) as Split
from TableA
)
Update a
set NewColumn = a.Split
from CTE_Split a
Now when I do this I get my table and it looks as such
IDNumber NewColumn
1 1
2 1
3 1
4 1
5 1
However when I do the select only I can see that I get the desire output, now I have done this before to split result sets into multiple groups and everything works within the select but now that I need to update the table I am getting this weird result. Not quiet sure what I'm doing wrong or if anyone can provide any sort of feedback.
So after a whole day of frustration I was able to compare this code and table to another that I had already done this process to. The reason that this table was getting updated to all 1s was because turns out that whoever made the table thought this was supposed to be a bit flag. When it reality it should be an int because in this case its actually only two possible values but in others it could be more than two.
Thank you for all your suggestion and help and it should teach me to scope out data types of tables when using the ntile function.
Try updating your table directly rather than updating your CTE. This makes it clearer what your UPDATE statement does.
Here is an example:
WITH CTE_Split AS
(
SELECT
*,
ntile(2) over (order by newid()) as Split
FROM TableA
)
UPDATE a
SET NewColumn = c.Split
FROM
TableA a
INNER JOIN CTE_Split c ON a.IDNumber = c.IDNumber
I assume that what you want to achieve is to group your records into two randomized partitions. This statement seems to do the job.

SQL Get Second Record

I am looking to retrieve only the second (duplicate) record from a data set. For example in the following picture:
Inside the UnitID column there is two separate records for 105. I only want the returned data set to return the second 105 record. Additionally, I want this query to return the second record for all duplicates, not just 105.
I have tried everything I can think of, albeit I am not that experience, and I cannot figure it out. Any help would be greatly appreciated.
You need to use GROUP BY for this.
Here's an example: (I can't read your first column name, so I'm calling it JobUnitK
SELECT MAX(JobUnitK), Unit
FROM JobUnits
WHERE DispatchDate = 'oct 4, 2015'
GROUP BY Unit
HAVING COUNT(*) > 1
I'm assuming JobUnitK is your ordering/id field. If it's not, just replace MAX(JobUnitK) with MAX(FieldIOrderWith).
Use RANK function. Rank the rows OVER PARTITION BY UnitId and pick the rows with rank 2 .
For reference -
https://msdn.microsoft.com/en-IN/library/ms176102.aspx
Assuming SQL Server 2005 and up, you can use the Row_Number windowing function:
WITH DupeCalc AS (
SELECT
DupID = Row_Number() OVER (PARTITION BY UnitID, ORDER BY JobUnitKeyID),
*
FROM JobUnits
WHERE DispatchDate = '20151004'
ORDER BY UnitID Desc
)
SELECT *
FROM DupeCalc
WHERE DupID >= 2
;
This is better than a solution that uses Max(JobUnitKeyID) for multiple reasons:
There could be more than one duplicate, in which case using Min(JobUnitKeyID) in conjunction with UnitID to join back on the UnitID where the JobUnitKeyID <> MinJobUnitKeyID` is required.
Except, using Min or Max requires you to join back to the same data (which will be inherently slower).
If the ordering key you use turns out to be non-unique, you won't be able to pull the right number of rows with either one.
If the ordering key consists of multiple columns, the query using Min or Max explodes in complexity.

Order select using the the values of in(....) SQL server

Is there any way to order the result (for example)
SELECT id,age, Location
FROM Student
WHERE Location in ('NY','DC','MI','TE')
instead of having them as
id age Location
1 2 DC
2 2 NY
4 2 MI
6 2 TE
The Result should be ordered as the order of in ('NY','DC','MI','TE'):
id age Location
2 2 NY
1 2 DC
4 2 MI
6 2 TE
I figured that It's possible by having a temp table and inserting the ('NY','DC','MI','TE') into this table. Then using a left join will order it as wanted.
Is there a better solution.
Please Advise.
In MySQL this can be done using FIND_IN_SET, which gives back the index of the occurrence of the first argument in the second, the latter being a string containing comma-separated values.
I think the same thing should be possible using CHARINDEX - give this a try:
SELECT id, age, location
FROM student
WHERE location IN ('NY','DC','MI','TE')
ORDER BY CHARINDEX (location, 'NY,DC,MI,TE')
Any solution would be roughly the equivalent of using a temp table. You could use a CTE or a subquery, but you are essentially using different syntax to perform the same operation.
Assuming you don't have to select city codes from a table, try some of the following:
ORDER BY FIELD(Location,'NY','DC','MI','TE')
ORDER BY FIND_IN_SET(Location,'NY,DC,MI,TE')
ORDER BY LOCATE(CONCAT(',',Location,','),',NY,DC,MI,TE,')
ORDER BY Location IN ('NY','DC','MI','TE')
IN could also accept a sub-query (ORDER BY Location IN SELECT city_code FROM ...)

Binding literal list to SQL query as column that would return 1 row?

I have an SQL query that returns a single row, I have a list of numbers that I need to have returned as individual rows with the single row data bound to their row.
for example here's what I'm trying to do
select a,b,c, barcode
from database
join ('12345', '67890',...) as barcode
where a=1 and b=2 and c=3
I need to do it this way due to the fact i'm modifying some code that's looking for a specific format to come from the query, and modifying the code to match the literal list I have is far more difficult than doing something like this
Example Output:
a b c barcode
- - - -------
1 2 3 12345
1 2 3 67890
1 2 3 ....
1 2 3 ....
...
Easiest method would be to create a barcode table with a single column, insert the values you want here one at a time, then join to that table.
Could use a union to fudge it as well. Problem with join ('484','48583',...) is you are joining to a single row with multi columns, when you want one row per record.
pseudo coded:
select a,b,c, barcode
from database
join (select 12345 union all select 289384 union all...)a as barcode
where a=1 and b=2 and c=3
Basically, you could pass the list as a single CSV string and transform it into a row set of items. A table-valued function is often used in such cases, but there are actually many options to explore. A very comprehensive collection of various methods and their tests can be found in the set of articles by Erland Sommarskog: Arrays and Lists in SQL Server.
If it was e.g. a function, your query might look like this:
SELECT
t.a,
t.b,
t.c,
s.Value AS barcode
FROM yourtable t
CROSS JOIN dbo.Split('12345,67890', ',') s
WHERE t.a = 1
AND t.b = 2
AND t.c = 3

Resources