Get value of a specific column of a row without primary key - sql

This is purely out of curiosity.
create table test (ename varchar(50))
insert into test values ('abcd')
insert into test values ('pqrs')
insert into test values ('lmno')
insert into test values ('xxxx')
insert into test values ('tops')
I want the value of 3rd row from this table in a variable. i.e "lmno"
If I do this :
Declare #value varchar(50)
Select #value = ename from
(
select Row_number() over(order by ename) Rowno, * from test
) X where Rowno=3
print #value
I will get pqrs.
I cannot use this:
Declare #value varchar(50)
Select #value = ename from
(
select Row_number() over(order by 1) Rowno, * from test
) X where Rowno=3
because
Windowed functions do not support integer indices as ORDER BY clause
expressions.
Any options?
EDIT :
If I query it as
Select * from test
I do get records in the order in which they were inserted. That means somewhere there is a record as to how they were inserted. I just want to capture this sequence.

You are making a very very poor assumption about RDBMS's. The order that a RDBMS stores records, or they order they are written to the table is 100% absolutely inconsequential. It means nothing. It's arbitrary and you can't rely on it.
You will need to either add a new column to be the 'order' that you desire, or you will have to better define why you want pqrs in your recordset since 3rd record is meaningless in this sense.
To your edit: There is no record of the order which the records were inserted. There is an order by which the records are returned to the record set, and that they naturally lay in the DB's structure underneath, but it is arbitrary. The reason you get them back in the order in which they were written is because you have a tiny little table on a RDBMS that stores data in a single spot. This fails as soon as you scale your architecture up. You can not and should never ever rely on the order that your RDBMS retrieves records.

Let's look at it step by step.
select ename from test order by ename;
This orders by ename.
select ename from test order by 1;
Here 1 is an alias for the 1st element in your select clause, which is ename. So you order by ename again.
select Row_number() over(order by ename) Rowno, * from test
The row_number function works on records ordered by ename.
select Row_number() over(order by 1) Rowno, * from test
What is 1 supposed to mean here? We are inside an over clause and there is no first element the 1 could refer to. So it is not allowed to use a number here (it would only be confusing, as it could only mean a literal 1 for every record which doesn't order anything).
As to "I do get records in the order in which they were inserted. That means somewhere there is a record as to how they were inserted. I just want to capture this sequence.": No, that isn't the case. Right now you happen to get the records in the order they were inserted, but this is in no way guaranteed. The only way to guarantee an order is to have fields to represent the desired order and use them in ORDER BY.

Try This code
create table test (ename varchar(50))
insert into test values ('abcd')
insert into test values ('pqrs')
insert into test values ('lmno')
insert into test values ('xxxx')
insert into test values ('tops')
SELECT *FROM (
SELECT ROW_NUMBER() over (order by HH) AS RNO,ename FROM (SELECT ENAME,'' AS HH FROM test) T)T1
WHERE RNO=3

WITH MyCte AS
(
SELECT *, row_number() OVER(ORDER BY (SELECT 0)) ID FROM test
)
SELECT *
FROM MyCte
WHERE ID = 3

Try:
create table #test (ename varchar(50))
insert into #test values ('abcd')
insert into #test values ('pqrs')
insert into #test values ('lmno')
insert into #test values ('xxxx')
insert into #test values ('tops')
CREATE TABLE #Temp(RowID INT PRIMARY KEY IDENTITY(1,1), ename VARCHAR(10))
INSERT INTO #temp(ename)
SELECT ename from #test
SELECT T2.*
FROM #temp T1
JOIN #test T2 ON T1.ename = T2.ename
WHERE T1.RowID = 3

Related

Oracle 11g: copying a column from one table to another adding an sequence ID. Dropping a sequence

I would like to copy the column values from one table to another doing something like (doesn't work):
create sequence INSTITUTION_SEQ
minvalue 1
maxvalue 999999999999999999999
start with 1
increment by 1
cache 20;
INSERT INTO INSTITUTION
(ID, NAME)
VALUES(
INSTITUTION_SEQ.nextval,
SELECT DISTINCT ACB_BANK_NAME FROM LUP ORDER BY ACB_BANK_NAME
);
Questions
What is the correct SQL query to launch in order to get the expected result ?
How can the sequence be dropped at the end of the script ? Tried DROP INSTITUTION_SEQ but cause an ORA-00950: invalid DROP option
Instead of create/use/drop a sequence you can get a sequential value using:
INSERT INTO INSTITUTION (ID, NAME)
SELECT ROW_NUMBER() OVER (ORDER BY ACB_BANK_NAME), ACB_BANK_NAME
FROM
( -- DISTINCT is processed after ROW_NUMBER
SELECT DISTINCT ACB_BANK_NAME FROM LUP
) dt;
INSERT INTO INSTITUTION (ID, NAME)
SELECT ROW_NUMBER() OVER (ORDER BY ACB_BANK_NAME), ACB_BANK_NAME
FROM LUP
GROUP BY ACB_BANK_NAME; -- GROUP BY is processed before ROW_NUMBER
You don't need to create and drop a sequence, you can use rownum or an analytic function:
INSERT INTO INSTITUTION (ID, NAME)
SELECT rownum, ACB_BANK_NAME
FROM (
SELECT DISTINCT ACB_BANK_NAME FROM LUP ORDER BY ACB_BANK_NAME
);

Unique ID using function with every record in Insert statement

I have a statement in stored procedure
INSERT into table(ID, name, age)
SELECT fnGetLowestFreeID(), name, age
FROM #tempdata
The function fnGetLowestFreeID() gets the lowest free ID of the table table.
I want to insert unique ID with every record in the table. I have tried iteration and transaction. But they aren't fitting the scenario.
I cannot use Identity Column. I have this restriction of using IDs between 0-4 and assigning the lowest free ID using that function. In case of returned ID greater than 4, the function is returning an error. Suppose there are already 1 and 2 in the table. The function will return 0 and I have to assign this ID to the new record, 3 to the next record and so on on the basis of number of records in the #tempdata.
try this
CREATE TABLE dbo.Tmp_City(Id int NOT NULL IDENTITY(1, 1),
Name varchar(50) , Country varchar(50), )
OR
ALTER TABLE dbo.Tmp_City
MODIFY COLUMN Id int NOT NULL IDENTITY(1, 1)
OR
Create a Sequence and assign Sequence.NEXTVAL as ID
in the insert statement
You can make use of a rank function like row_number and do something like this.
INSERT into table(ID, name, age)
SELECT row_number() over (order by id) + fnGetLowestFreeID(), name, age
FROM #tempdata
Here are 3 scenarios-
1)Show the function which you are using
2) Doesn't make sense to use a function and make it unique
still- you can use rank-
INSERT into table(ID, name, age)
SELECT row_number() over (order by id) + fnGetLowestFreeID(), name, age
FROM #tempdata
3)Else, get rid of function and use max(id)+1 because you dont want to use identitiy column
You could use a Numbers table to join the query doing your insert. You can google the concept for more info, but essentially you create a table (for example with the name "Numbers") with a single column "nums" of some integer type, and then you add some amount of rows, starting with 0 or 1, and going as far as you need. For example, you could end with this table content:
nums
----
0
1
2
3
4
5
6
Then you can use such a table to modify your insert, you don't need the function anymore:
INSERT into table(ID, name, age)
SELECT t2.nums, t.name, t.age
FROM (
SELECT name, age, row_number() over (order by name) as seq
FROM #tempdata
) t
INNER JOIN (
SELECT n.nums, row_number() over (order by n.nums) as seq
FROM Numbers n
WHERE n.nums < 5 AND NOT EXISTS (
SELECT * FROM table WHERE table.ID = n.nums
)
) t2 ON t.seq = t2.seq
Now, this query leaves out one of your requirements, that would be launching an error when no slots are available, but that is easy to fix. You can do previously a query and test if the count of records in table plus the sum of records in #tempdata is higher than 5. If so, you launch the error as you know there would not be enough free slots for the records in #tempdata.
Side note: table looks like a terrible name for a table, I hope that in your real code you have a meaningful name :)

Assign ID to each row in Microsoft sql server view

Lets say I have a table like this:
create table MyTable (
Myname varchar (10) primary key not null
)
and a few row of data like:
insert into MyTable values ('john');
insert into MyTable values ('Brad');
insert into MyTable values ('James');
insert into MyTable values ('Anna');
insert into MyTable values ('Eric');
insert into MyTable values ('Hossein');
I want to create a view that assign an ID to each row,
I have used the select statement below :
select rank() OVER (ORDER BY Myname) as ID, MyTable.Myname
from MyTable
order by ID
The results is quite acceptable, But the problem come out when I try to create view
create view myview as
select rank() OVER (ORDER BY Myname) as ID, MyTable.Myname
from MyTable
order by ID
My questions are:
1- how can I create the view from the select statement mentioned above?
2- Is there any alternative way that I can use?
order by is not allowed in a view unless you use top. As per the documentation:
The SELECT clauses in a view definition cannot include the following:
An ORDER BY clause, unless there is also a TOP clause in the select list of the SELECT statement
So, your statement is fine without the order by:
create view myview as
select rank() OVER (ORDER BY Myname) as ID, MyTable.Myname
from MyTable ;
Even if you include the order by with a top, the results are not guaranteed in a particular order. You can only guarantee that by using order by the outer query.

remove duplicate records with a criteria

I am using a script which requires only unique values. And I have a table which has duplicates like below, i need to keep only unique values (first occurrence) irrespective of what is present inside the brackets.
can I delete the records and keep the unique records using a single query?
Input table
ID Name
1 (Del)testing
2 (Del)test
3 (Delete)testing
4 (Delete)tester
5 (Del)tst
6 (Delete)tst
So the output tables should be something like
Input table
ID Name
1 (Del)testing
2 (Del)test
3 (Delete) tester
4 (Del)tst
SELECT DISTINCT * FROM FOO;
It depends how much data you have to retrieve, if you only have to change Delete -> Del you can try with REPLACE
http://technet.microsoft.com/en-us/library/ms186862.aspx
also grouping functions should help you
I don't think this would be easy query
Assumption: The name column always has all strings in the format given in the sample data.
Try this:
;with cte as
(select *, rank() over
(partition by substring(name, charindex(')',name)+1,len(name)+1 - charindex(')',name))
order by id) rn
from tbl
),
filtered_cte as
(select * from cte
where rn = 1
)
select rank() over (partition by getdate() order by id,getdate()) id , name
from filtered_cte
How this works:
The first CTE cte uses rank() to rank the occurrence of the string outside brackets in the name column.
The second CTE filtered_cte only returns the first row for each occurence of the specified string. In this step, we get the expected results, but not in the desired format.
In this step we partition by and order by the getdate() function. This function is chosen as a dummy to give us continuous values for the id column while using the rank function as we did in step 1.
Demo here.
Note that this solution will return filtered values, but not delete anything in the source table. If you wish, you can delete from the CTE created in step 1 to remove data from the source table.
First use this update to make them uniform
Update table set name = replace(Name, '(Del)' , '(Delete)')
then delete the repetitive names
Delete from table where id in
(Select id from (Select Row_Number() over(Partition by Name order by id) as rn,* from table) x
where rn > 1)
First create the input date table
CREATE TABLE test
(ID int,Name varchar(20));
INSERT INTO test
(`ID`, `Name`)
VALUES
(1, '(Del)testing'),
(2, '(Del)test'),
(3, '(Delete)testing'),
(4, '(Delete)tester'),
(5, '(Del)tst'),
(6, '(Delete)tst');
Select Query
select id, name
from (
select id, name ,
ROW_NUMBER() OVER(PARTITION BY substring(name,PATINDEX('%)%',name)+1,20) ORDER BY name) rn
from test ) t
where rn= 1
order by 1
SQL Fiddle Link
http://www.sqlfiddle.com/#!6/a02b0/34

Update behaviour

When I made a mistake in update query text, I spotted unpredictable query result. Here is query text for update.
DECLARE #T TABLE (Id int,[Name] nvarchar(100),RNA int)
INSERT INTO #T(Id,[Name])
SELECT [Id],[Name]
FROM (VALUES (1,N'D'),
(2,N'B'),
(3,N'S'),
(4,N'A'),
(5,N'F')
) AS vtable([Id],[Name])
UPDATE #T
SET RNA = T.RN
FROM (
select PP.Name,ROW_NUMBER() OVER(ORDER BY PP.Name) RN,PP.RNA from #T PP
) T
select * from #T
I know where mistake was made:
UPDATE #T
should be
UPDATE T
But why result (with "bad" query) looks like:
Id Name RNA
---- ----- -------
1 D 1
2 B 5
3 S 1
4 A 5
5 F 1
I suspect that 1 and 5 values are MIN(Id) and MAX(Id).
Execution plan look like:
Will this situation be the same in every situation with this kind of mistake?
If yes, has this behaviour any practical value?
The situation will not be the same for every kind of mistake. You have a non-determinisitic update statement, that is to say theoritically any of the values for RN in your subquery T could be applied to any of the values in #T. You are essentially running the UPDATE version of this:
SELECT *
FROM #t a
CROSS JOIN
( SELECT TOP 1
PP.Name,
ROW_NUMBER() OVER(ORDER BY PP.Name) RN,
PP.RNA
FROM #T PP
ORDER BY NEWID()
) T
OPTION (FORCE ORDER);
The online manual states:
The results of an UPDATE statement are undefined if the statement
includes a FROM clause that is not specified in such a way that only
one value is available for each column occurrence that is updated,
that is if the UPDATE statement is not deterministic.
What is slightly interesting is that if you run the above you will get a different result each time (barring the 1/25 chance of getting the same result twice in a row), if you remove the random sorting using NEWID() you will get the same value of RN for each row, but the update consistently returns the same results, with 2 different RNs. I am not surprised the result remains consistent with no random ordering because with no changes to the data, and no random factor introduced I would expect the optimiser to come up with the same execution plan no matter how many times it is run.
Since no explicit ordering is specified in your update query, the order is due to the order of the records on the leaf, if the order of the records is altered the result is altered. This can be shown by inserting the records of #T into a new table with different IDs
DECLARE #T2 TABLE (Id int,[Name] nvarchar(100),RNA int);
INSERT #T2
SELECT id, Name, NULL
FROM #T
ORDER BY ROW_NUMBER() OVER(ORDER BY NEWID())
OPTION (FORCE ORDER);
UPDATE #T2
SET RNA = T.RN
FROM (
select PP.Name,ROW_NUMBER() OVER(ORDER BY PP.Name) RN,PP.RNA from #T2 PP
) T
SELECT *
FROM #T2;
I can see no reason why this is always the min or max value of RN though, I expect you would have to delve deep into the optimiser to find this. Which is probably a new question better suited for the dba stack exchange.