How to use generate Id for different values in calculated columns? - sql-server

I have a big query (which is already ordered as per my needs), one of the columns is calculated (varchar combination of other columns in the query). I need an incremental integer to identify this calculated column (duplicates should have the same id).
I canĀ“t use rank because the order in which I need the incremental number uses another criteria than the one used to generate the calculated column.
This is what I need:
OrderByColumn CalculatedColumn GeneratedId
1 ggg 1
1 aaa 2
1 ggg 1
1 fff 3
2 vvv 4
2 ddd 5
3 ggg 1
4 rrr 6
5 aaa 2
5 ooo 7
5 kkk 8
8 vvv 4
9 aaa 2

Use
ROW_NUMBER() OVER (PARTITION BY XXX ORDER BY YYY)
assuming you are using SQL2005 or better
http://msdn.microsoft.com/en-us/library/ms186734.aspx
-- though like you said this doesn't solve your dupes with same ID thing - ahhh! Give me a moment - should be able to do this pretty easy
Edit:
Here you go -
http://sqlfiddle.com/#!3/2f014/2
-- Select stuff:
select vals.val as genid, ord.* from ord
-- Join back to a distinct list of CalculatedColumn with a row_number() to id them
inner join
(select calculatedcolumn, row_number() over (order by calculatedcolumn) as val from ord group by calculatedcolumn) as vals on vals.calculatedcolumn = ord.calculatedcolumn
order by ord.orderbycolumn
Of course this is using the calculated column in the subquery - so you will need to re-calculate unless you store the value in a temp table or table variable

Related

How to split Row into multiple column using T-SQL

There are three column,wherever D_ID=13,value_amount holds value for mode of payment and wherever D_ID=10,value_amount holds value for amount.
ID D_ID Value_amount
1 13 2
1 13 2
1 10 1500
1 10 1500
2 13 1
2 13 1
2 10 2000
2 10 2000
Now I have to add two more columns amount and mode_of_payment and result should come like below
ID amount mode_of_payment
1 1500 2
1 1500 2
2 2000 1
2 2000 1
This is too long for a comment.
Simply put, your data is severely flawed. For the example data you've given, you're "ok", because the rows have the same values to the same ID, but what about when they don't? Let's assume, for example, we have data that looks like this:
ID D_ID Value_amount
1 13 1 --1
1 13 2 --2
1 10 1500 --3
1 10 1000 --4
2 13 1 --5
2 13 2 --6
2 10 2000 --7
2 10 3000 --8
I've added a "row number" next to data, for demonstration purposes only.
Here, what row is row "1" related to? Row "3" or row "4"? How do you know? There's no always ascending value in your data, so row "3" could just as easily be row "4". In fact, if we were to order the data using ID ASC, D_ID DESC, Value_amount ASC then rows 3 and 4 would "swap" in order. This could mean that when you attempt a solution, the order in wrong.
Tables aren't stored in any particular order, that are unordered. What determines the order the data is presented in is the ORDER BY clause, and if you don't have a value to define that "order", then that "order" is lost as soon as you INSERT it.
If, however, we add a always ascending value into your data, you can achieve this.
CREATE TABLE dbo.YourTable (UID int IDENTITY,
ID int,
DID int,
Value_amount int);
GO
INSERT INTO dbo.YourTable (ID, DID, Value_amount)
VALUES (1,13,1 ),
(1,13,2 ),
(1,10,1500),
(1,10,1000),
(2,13,1 ),
(2,13,2 ),
(2,10,2000),
(2,10,3000);
GO
WITH RNs AS(
SELECT ID,
DID,
Value_amount,
ROW_NUMBER() OVER (PARTITION BY ID, DID ORDER BY UID ASC) AS RN
FROM dbo.YourTable)
SELECT ID,
MAX(CASE DID WHEN 13 THEN Value_Amount END) AS Amount,
MAX(CASE DID WHEN 10 THEN Value_Amount END) AS PaymentMode
FROM RNs
GROUP BY RN,
ID;
GO
DROP TABLE dbo.YourTable;
Of course, you need to fix your design to implement this, but you need to do that anyway.

Increment column values based on two columns in Oracle database

I have a table Test , that has below structure:
Id CID RO Other Columns
1 111 2
2 111 1
3 111 6
4 111 6
5 111 8
6 111 5
7 101 4
8 101 4
9 101 3
Resultant order in RO should be like below:
-> For One CID and ascending order of RO should get order (RO) replaced with 1,2,3,4 and so on
Final Order in RO column:
(RO column's value got replaced)
Id CID RO (New) RO Other Columns
1 111 2 2
2 111 1 1
3 111 6 4
4 111 6 5
5 111 8 6
6 111 5 3
7 101 4 2
8 101 4 3
9 101 3 1
There are hundreds of cids like that in table. Please let me know if this can be achieved in single query using some Oracle function or some procedure needs to be written. Any lead or example would be helpful.
Thanks
The NEW_RO column can be calculated with the analytic function ROW_NUMBER():
select ... ,
row_number() over (partition by cid order by ro) as new_ro [, ...]
In your data, there are ties for RO within the same CID. Do you care, in that case, which row gets what NEW_RO value? If, for example, in the case of same RO you also want to (further) order by ID, you can change the above to
select ... ,
row_number() over (partition by cid order by ro, id) as new_ro [, ...]
EDIT: I missed the fact that you need to UPDATE the RO values with the NEW_RO values. Analytic functions can't be used in an UPDATE statement (not directly anyway); the MERGE statement is the perfect alternative for this:
merge into test
using ( select id,
row_number() over (partition by cid order by ro, id) as new_ro
from test
) s
on (test.id = s.id)
when matched then update set ro = s.new_ro
;
Addressing the follow up question in the comment on #mathguy's answer. If you've got a query that is producing the new values you want, and want to quickly write an update, I like to use merge:
MERGE INTO your_table target
USING (your_query_here) source
ON (Target.ID = Source.ID)
WHEN MATCHED THEN
UPDATE SET Target.column = Source.new_value
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606
MERGE can do more than that, but I've found it handy in this "correct the data" situation.

SQL Server query to display all columns but with distinct values in one of the columns (not grouping anything)

I have a table with 106 columns. One of those columns is a "Type" column with 16 types.
I want 16 rows, where the Type is distinct. So, row 1 has a type of "Construction", row 2 has a type of "Elevator PVT", etc.
Using Navicat.
From what I've found (and understood) so far, I can't use Distinct (because that looks across all rows), I can't use Group By (because that's for aggregating data, which I'm not looking to do), so I'm stuck.
Please be gentle- I'm really really new at this.
Below is a part of the table (how can I share this normally?)- it's really big so I didn't share the whole thing. Below is a partial result I'm looking for, where the Violation_Type is unique and the rest of the columns display.
Got it.. Sheesh... (took me forever, but got it...)
D_ID B_ID V_ID V_Type S_ID c_f d_y l_u p_s du_p
------ ------ ------- -------------- ------ ----- ------ ------ ----- ------
184 117 V 032 Elevator PVT 2 8 0 0
4 140 V 100 Construction 1 8 0 0
10 116 V 122 Electric 1 8 2005 0 0
11 117 V 033 Boiler Local 1 0 2005 0 0
You can use ROW_NUMBER for this:
SELECT *
FROM(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY V_Type ORDER BY (SELECT NULL))
FROM tbl
)t
WHERE rn = 1
Modify the ORDER BY depending on what row you want to prioritize.
From the documentation:
Returns the sequential number of a row within a partition of a result
set, starting at 1 for the first row in each partition.
This means that for every row within a partition (specified by the PARTITION BY clause), sql-server assigns a number from 1 depending on the order specified in the ORDER BY clause.
ROW_NUMBER requires an ORDER BY clause. SELECT NULL tells the sql-server that we do not want to enforce a particular order. We just want the rows numbered by partition.
The WHERE rn = 1 obviously filters only rows that has a ROW_NUMBER of 1. This gives you one row for every V_TYPE available.

T-SQL select rows by oldest date and unique category

I'm using Microsoft SQL. I have a table that contains information stored by two different categories and a date. For example:
ID Cat1 Cat2 Date/Time Data
1 1 A 11:00 456
2 1 B 11:01 789
3 1 A 11:01 123
4 2 A 11:05 987
5 2 B 11:06 654
6 1 A 11:06 321
I want to extract one line for each unique combination of Cat1 and Cat2 and I need the line with the oldest date. In the above I want ID = 1, 2, 4, and 5.
Thanks
Have a look at row_number() on MSDN.
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY date_time, id) rn
FROM mytable
) q
WHERE rn = 1
(run the code on SQL Fiddle)
Quassnoi's answer is fine, but I'm a bit uncomfortable with how it handles dups. It seems to return based on insertion order, but I'm not sure if even that can be guaranteed? (see these two fiddles for an example where the result changes based on insertion order: dup at the end, dup at the beginning)
Plus, I kinda like staying with old-school SQL when I can, so I would do it this way (see this fiddle for how it handles dups):
select *
from my_table t1
left join my_table t2
on t1.cat1 = t2.cat1
and t1.cat2 = t2.cat2
and t1.datetime > t2.datetime
where t2.datetime is null

Perform a double sort on two columns in SQL Server with a primary sort not in the evaluation of the first two

I have a table using SQL Server 2008 it has a table with two sortable columns on it one is manually set and the other is calculated by a system procedure (this procedure sorts everything as a whole and assigns a sort starting at 10 until the highest row number times 10)
ID Manual System
------------------------
1 null 300
2 2 380
3 null 500
4 null 200
And I am trying to get it to sort the ids to be 4,2,1,3
I would like the output to take the Manual Sort over the System when it has been applied. to further complicate things if another row is added and it has a manual sort that also needs to be considered.
ID Manual System
-----------------------
1 null 300
2 2 380
3 null 500
4 null 200
5 5 100
so the new sort would be 4,2,1,3,5
ID Manual System
-----------------------
4 null 200
2 2 380
1 null 300
3 null 200
5 5 100
Any ideas? and I have tried Rank, Dense_Rank, Row_Number etc.
The solutions that have been given seem correct for my example. I forgot to mention there is a third column personID that is also a factor here.
ID Manual System PersonID
-------------------------------------
4 null 200 22
2 2 380 22
1 null 300 22
3 null 200 22
5 5 100 22
8 1 210 25
6 1 480 25
7 null 600 25
9 4 800 25
10 null 990 25
So I first have to order them by person then, order them by Manual then by sort. which still seems to give me an issue.
Here is my solution: http://sqlfiddle.com/#!3/a32a0/1/0
SELECT *
FROM
(
SELECT
ID
, ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY System)-.1 AS rn
, Manual
, System
, PersonID
FROM YourTable
) t0
ORDER BY PersonID
, COALESCE(Manual, RN)
Here is the explanation:
We are taking the row number as the base row number. But since we first order by a higher-order index of PersonID, I PARTITION BY... before I ORDER BY... this resets the index for each grouping of MANUAL
In the case of a tie between the natural ordering of the ROW_NUMBER and the MANUAL sorting, I subtract .1 (arbitrary amount between (0,1)). This gives preference to the MANUAL value in case of a tie
When it comes to ordering the final result, I ORDER BY the PARTITION BY value first, ensuring the proper grouping first, then I order by the first non-null value of MANUAL and RN
Give it a try. +points to the starting points of the previous two answers. I used one of them as a starting point and re-wrote from there.
EDIT: Removed the subtraction of .1 and added a new ranking function which "tricks" the optimizer into preferring manual over rank. I have no idea if this holds up in all cases or if the optimizer will fail to give the results in this order under other circumstances, but I wanted to include the findings just in case they're helpful.
My updated query is as follows:
SELECT *
FROM
(
SELECT
ID
, ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY System) AS rn
, ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY Manual) AS rn_throwaway
, Manual
, System
, PersonID
FROM YourTable
) t0
ORDER BY PersonID
, COALESCE(Manual, RN)
And examples of it in use are at http://sqlfiddle.com/#!3/1831d/55/0 and http://sqlfiddle.com/#!3/a32a0/9/0
If I'm understanding your requirements, you want to sort by the System column, unless the Manual column is supplied, and in which case, use that as the sort position instead? If so, then this should work for you using CASE and ROW_NUMBER:
SELECT Id, Manual, System
FROM (
SELECT Id,
Manual,
System,
ROW_NUMBER() OVER (ORDER BY Manual, System) rn
FROM YourTable) t
ORDER BY CASE WHEN Manual IS NULL THEN RN ELSE Manual END, COALESCE(Manual,RN+1)
SQL Fiddle Demo
I think this is what you need. It is bit difficult to explain.
Basically inserting not null manual values as row index (or row number) to the record list ordered by system.
FIDDLE DEMO
;with cte as (
select id, manual,system,
convert(decimal(10,1),row_number() over(order by system)) rn
from t
where manual is null
union all
select id, manual,system, convert(decimal(10,1),manual-0.5) rn
from t
where manual is not null
)
select id,manual,system
from cte
order by rn
| ID | MANUAL | SYSTEM |
------------------------
| 4 | (null) | 200 |
| 2 | 2 | 380 |
| 1 | (null) | 300 |
| 3 | (null) | 500 |
| 5 | 5 | 100 |

Resources