SQL Server - Update a whole column using Order By - sql

I'm using SQL Server and have Management Studio installed if this is relevant.
I would like to copy a whole column from one table to another, but the catch is that the table I must copy to needs to be ordered a certain way, as there is no common identity between these tables I could use to join them.
I have read these two questions:
Copy data from one column to other column (which is in a different table)
SQL Server: UPDATE a table by using ORDER BY
and I tried to combine their answers as follows:
WITH cte AS
(
-- I must specify TOP to use ORDER BY
SELECT TOP(50000) *
FROM TableToCopyTo
ORDER BY ColumnUsedToOrder
)
UPDATE cte
SET ColumnToCopyTo = (SELECT ColumnToCopyFrom FROM TableToCopyFrom)
When I try to execute this query, it returns the following error:
Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I've tried looking up the error but couldn't find relevant information.
I would like to either understand why my query is wrong or find an alternative to achieve what I'm looking for.

Try the following answer. You have to give the matching columns for the two tables.
;WITH cte AS
(
-- I must specify TOP to use ORDER BY
SELECT TOP(50000) *
FROM TableToCopyTo
ORDER BY ColumnUsedToOrder
)
UPDATE cte SET ColumnToCopyTo = ColumnToCopyFrom
FROM cte
JOIN TableToCopyFrom A
ON cte.ColumnName = A.EquvaliantColumnName

Provided that your TableToCopyFrom has equal to or more rows than TableToCopyTo I'd use something like:
WITH cte1 AS
(
-- I must specify TOP to use ORDER BY
SELECT TOP(50000) *
, ROW_NUMBER() OVER (ORDER BY ColumnUsedToOrder) AS RwNr
FROM TableToCopyTo
ORDER BY ColumnUsedToOrder
), cte2 AS (
SELECT *
, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RwNr -- or order by a column you know makes sense; this way it'll order it the way the data "sits" in the table, but it's not guaranteed
FROM TableToCopyFrom
)
UPDATE cte1
SET ColumnToCopyTo = ColumnToCopyFrom
FROM cte1
INNER JOIN cte2 ON cte1.RwNr = cte2.RwNr;
EDIT: please make sure you test this logic on some copy of the database first of course.

you can use Row number as key as follows
WITH tblDest AS
(
SELECT Row_Number() over(order by ColumnUsedToOrder) as RowNum, TOP(50000) *
FROM TableToCopyTo
ORDER BY ColumnUsedToOrder
),tblSrc as
(
select Row_Number() over(order by (select null)) as RowNum,ColumntoCopyFrom
from TableToCopyFrom
)
update tblDest set ColumntoCopyto = ColumntoCopyFrom
FROM tblDest join tblSrc on tblDest.RowNum = tblSrc.RowNum

Related

Listing multiple columns in a single row in SQL

(select ID,EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE,ROW_NUMBER() OVER(PARTITION BY EXTERNAL_TRANSACTION_ID ORDER BY ID ) AS SEQNUM
from AC_POS_TRANSACTION_TRK aptt WHERE [RESULT] ='Success'
GROUP BY ID, EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE )
Hello,
On above query, I want to get rows of transaction id's which has seqnum=1 and seqnum=2
But if that transaction id has no second row (seqnum=2), I dont want to get any row for that transaction id.
Thanks!!
Something like this
Not 100% sure if this is correct without you table definition, but my understanding is that you want to EXCLUDE records if that record has an entry with seqnum=2 -- you can't use a where clause alone because that would still return seqnum = 1.
You can use an exists /not exists or in/not in clause like this
(select ID,EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE,ROW_NUMBER() OVER(PARTITION BY EXTERNAL_TRANSACTION_ID ORDER BY ID ) AS SEQNUM
from AC_POS_TRANSACTION_TRK aptt WHERE [RESULT] ='Success'
and not exists ( select 1 from AC_POS_TRANSACTION_TRK a where a.id = aptt.id
and a.seqnum = 2)
GROUP BY ID, EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE )
basically what this does is it excludes records if a record exists as specified in the NOT EXISTS query.
One option you can try is to add a count of rows per group using the same partioning critera and then filter accordingly. Not entirely sure about your query without seeing it in context and with sample data - there's no aggregation so why use group by?
However can you try something along these lines
select * from (
select ID,EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE,
Row_Number() over(partition by EXTERNAL_TRANSACTION_ID order by ID) as SEQNUM,
Count(*) over(partition by EXTERNAL_TRANSACTION_ID) Qty
from AC_POS_TRANSACTION_TRK
where [RESULT] ='Success'
)x
where SEQNUM in (1,2) and Qty>1
This should do the job.
With Qry As (
-- Your original query goes here
),
Select Qry.*
From Qry
Where Exists (
Select *
From Qry Qry1
Where Qry1.EXTERNAL_TRANSACTION_ID = Qry.EXTERNAL_TRANSACTION_ID
And Qry1.SEQNUM = 1
)
And Exists (
Select *
From Qry Qry2
Where Qry2.EXTERNAL_TRANSACTION_ID = Qry.EXTERNAL_TRANSACTION_ID
And Qry2.SEQNUM = 2
)
BTW, your original query looks problematic to me, specifically I think that instead of a GROUP BY columns those columns should be in the PARTITION BY clause of the OVER statement, but without knowing more about the table structures and what you're trying to achieve, I could not say for sure.

How to join in SQL-SERVER

I am trying to learn SQL-SERVER and I have created the below query:
WITH T AS
(
SELECT ROW_NUMBER() OVER(ORDER BY d.DIALOG_ID) as row_num, *
FROM test.db as d
INNER JOIN test.dbs as ds
ON d.DIALOG_ID = ds.DIALOG_ID
)
SELECT *
FROM T
WHERE row_num <=10;
I found that the only way to limit is with ROW_NUMBER().
Although when I try to run the join I have this error:
org.jkiss.dbeaver.model.sql.DBSQLException: SQL Error [8156] [S0001]: The column 'DIALOG_ID' was specified multiple times for 'T'.
The problem: In the WITH, you do SELECT * which gets all columns from both tables db and dbs. Both have a column DIALOG_ID, so a column by that name ends up twice in the result set of the WITH.
Although until here that is all allowed, it is not good practice: why have the same data twice?
Things go wrong when SQL Server has to determine what SELECT * FROM T means: it expands SELECT * to the actual columns of T, but it finds a duplicate column name, and then it refuses to continue.
The fix (and also highly recommended in general): be specific about the columns that you want to output. If T has no duplicate columns, then SELECT * FROM T will succeed.
Note that the even-more-pure variant is to also be specific about what columns you select from T. By doing that it becomes clear at a glance what the SELECT produces, instead of having to guess or investigate when you look at the query later on (or when someone else does).
The updated code would look like this (fill in your column names as we don't know them):
WITH T AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY d.DIALOG_ID) as row_num,
d.DIALOG_ID, d.SOME_OTHER_COL,
ds.DS_ID, ds.SOME_OTHER_COL_2
FROM test.db AS d
INNER JOIN test.dbs AS ds ON d.DIALOG_ID = ds.DIALOG_ID
)
SELECT row_num, DIALOG_ID, SOME_OTHER_COL, DS_ID, SOME_OTHER_COL_2
FROM T
WHERE row_num <= 10;
WITH T AS
(
SELECT ROW_NUMBER() OVER(ORDER BY d.DIALOG_ID) as row_num, d.*
FROM test.db as d
INNER JOIN test.dbs as ds
ON d.DIALOG_ID = ds.DIALOG_ID
)
SELECT *
FROM T
WHERE row_num <=10;

Using row_number() in subquery results in ORA-00913: too many values

In Oracle, I wish to do something like the SQL below. For each row in "criteria," I want to find the latest row in another table (by last_modified_date) for the same location_id, and use that value to set default_start_interval. Or, if there is no such value, then use 30. However, as you can see, the subquery must have two values in the select statement to use row_number(). That causes an error. How do I reformat it so that it works?
update criteria pc set default_start_interval =
COALESCE(
(SELECT start_interval,
row_number() over(partition by aday.location_id
order by atime.last_modified_date desc
) as rn
FROM available_time atime
JOIN available_day aday ON aday.available_day_id = atime.available_day_id
WHERE aday.location_id = pc.location_id
and rn = 1)
, 30)
There are two issues in your update query:
The update expects only one value per row for default_start_interval, however, you have two columns in the select list.
The row number should be assigned before in the inner query, and then apply filter where rn = 1 in outer query.
Your update query should look like:
UPDATE criteria pc
SET default_start_interval = NVL(
(
SELECT start_interval FROM(
SELECT
start_interval, ROW_NUMBER() OVER(
PARTITION BY aday.location_id
ORDER BY atime.last_modified_date DESC
) AS rn
FROM
available_time atime
JOIN available_day aday ON aday.available_day_id = atime.available_day_id
WHERE
aday.location_id = pc.location_id
)
WHERE rn = 1)
, 30)
Note: You could simply use NVL instead of COALESCE as you only have one value to check for NULL. COALESCE is useful when you have multiple expressions.
I think a simpler method uses aggregation and keep to get the value you want:
update criteria pc
set default_start_interval =
(select coalesce(max(start_interval) keep (dense_rank first order by atime.last_modified_date desc), 30)
from available_time atime join
available_day aday
on aday.available_day_id = atime.available_day_id
where aday.location_id = pc.location_id
);
An aggregation query with no GROUP always returns one row. If no rows match, then the returned value is NULL -- the COALESCE() captures this case.

How to user 2 condition in an select statement?

I want that select name and family of user in a table that User-Id (in this)
is FK in another table. my conditions are:
1.[User-Id](in this table) = [Resume-Owner-Id] in other table.
2.Row number is between 2 value that get from asp program.
I write below code. but this has error.
SELECT *
FROM (SELECT Family,
Name,
Row_number()
OVER(
ORDER BY [User-Id] DESC) AS RowNum
FROM [Members-Description-Tbl]) AS People
WHERE [User-Id] = (SELECT [Resume-Owner-Id]
FROM [Resume-Tbl])
AND ( RowNum BETWEEN #StartRowIndex + 1 AND #MaxRows );
This is your query:
Select *
From (select Family, Name,
ROW_NUMBER() OVER (ORDER BY [User-Id] desc) AS RowNum
from [Members-Description-Tbl]
) As People
Where [User-Id] = (select [Resume-Owner-Id]
from [Resume-Tbl]
) AND
RowNum between #StartRowIndex+1 and #MaxRows
Such a query could get many errors -- variables could be undefined, tables not present, columns not defined.
One potential error that stands out is the = in the where clause. If the subquery returns more than one row, it will generate an error. The simple solution is to use in instead of =.
Thanks for your answer Gordon Linoffred.
Thanks for your answer Pradeep.
That's right. Your answers are right. But my query rise previous error again. I read my query again and understand that I must write [People].RowNum or RowNum instead of [User-Id]. This works now. Thanks again. :)

use Row_number after applying distinct

I am creating an SP which gives some result by applying distinct on it, now I want to implement sever side paging, so I tried using Row_number on distinct result like:
WITH CTE AS
(
SELECT ROW_NUMBER() OVER(ORDER BY tblA.TeamName DESC)
as Row,tblA.TeamId,tblA.TeamName,tblA.CompId,tblA.CompName,tblA.Title,tblA.Thumbnail,tblA.Rank,tblA.CountryId,tblA.CountryName
FROM
(
--The table query starts with SELECT
)tblA
)
SELECT CTE.* FROM CTE
WHERE CTE.Row BETWEEN #StartRowIndex AND #StartRowIndex+#NumRows-1
ORDER BY CTE.CountryName
but rows are first assigned RowNumber then distinct get applied that is why I am getting duplicate values, how to get distinct rows first then get row numbers for the same.
Any solution on this? Am I missing something?
need answer ASAP.
thanks in advance!
Don't you need to add "partition by" to your ROW_NUMBER statement?
ROW_NUMBER() OVER(Partition by ___, ___, ORDER BY tblA.TeamName DESC)
In the blank spaces, place the column names you would like to create a new row number for. Duplicates will receive a number that is NOT 1 so you might not need the distinct.
To gather the unique values you could write a subquery where the stored procedure only grabs the rows with a 1 in them.
select * from
(
your code
) where row = 1
Hope that helps.
I'm not sure why you're doing this:
WHERE CTE.Row BETWEEN #StartRowIndex AND #StartRowIndex+#NumRows-1