Selecting a sequence NEXTVAL for multiple rows - sql

I am building a SQL Server job to pull data from SQL Server into an Oracle database through a linked server. The table I need to populate has a sequence for the name ID, which is my primary key. I'm having trouble figuring out a way to do this simply, without some lengthy code. Here's what I have so far for the SELECT portion (some actual names obfuscated):
SELECT (SELECT NEXTVAL FROM OPENQUERY(MYSERVER,
'SELECT ORCL.NAME_SEQNO.NEXTVAL FROM DUAL')),
psn.BirthDate, psn.FirstName,
psn.MiddleName, psn.LastName, c.REGION_CODE
FROM Person psn
LEFT JOIN MYSERVER..ORCL.COUNTRY c ON c.COUNTRY_CODE = psn.Country
MYSERVER is the linked Oracle server, ORCL is obviously the schema. Person is a local table on the SQL Server database where the query is being executed.
When I run this query, I get the same exact value for all records for the NEXTVAL. What I need is for it to generate a new value for each returned record.
I found this similar question, with its answers, but am unsure how to apply it to my case (if even possible): Query several NEXTVAL from sequence in one statement

put it in a SQL scalar function. Example:
CREATE function [dbo].SEQ_PERSON()
returns bigint as
begin
return
( select NEXTVAL
from openquery(oraLinkedServer, 'select SEQ_PERSON.NEXTVAL FROM DUAL')
)
end

I ended up having to iterate through all the records and set the ID value individually. Messy and slow, but it seems to be the only option in this scenario.

Very easy Just Use a CURSOR to Iterate with the code :
SELECT NEXTVAL AS SQ from OPENQUERY(MYSERVER, 'SELECT AC2012.NAME_SEQNO.NEXTVAL FROM DUAL')
So you can embed this select statement in any Sql statement, and Iterate by the CURSOR.
PS:
DECLARE SQCURS CURSOR
FOR SELECT (SELECT NEXTVAL AS SQ FROM OPENQUERY(MYSERVER,
'SELECT ORCL.NAME_SEQNO.NEXTVAL FROM DUAL')),
psn.BirthDate, psn.FirstName, psn.MiddleName, psn.LastName, c.REGION_CODE
FROM Person psn
LEFT JOIN MYSERVER..ORCL.COUNTRY c ON c.COUNTRY_CODE = psn.Country
OPEN SQCURS
FETCH NEXT FROM SQCURS ;
I hope that help

Related

Can you force SQL Server to send the WHERE clause to Linked Server?

I'm trying to determine if a table in my SQL Server 2012 database has any records that don't exist in a table that's on a linked Oracle 11g database.
I tried to do this with the following:
select 1
from my_order_table ord
where not exists (select 1
from LINK_ORA..[SCHEMA1].[ORDERS]
where doc_id = ord.document_id)
and document_id = 'N2324JKL3511'
The issue is that it never completes because the ORDERS table on the linked server has about 100 million rows and as per the explain plan on SQL Server, it is trying to pull back the entire ORDERS table from the linked server and then apply the WHERE clause.
As per the explain plan, it views the remote table as having an estimated 10000 rows - I assume that's some kind of default if it is unable to get statistics..?
Even running something as simple as this:
select 1 from LINK_ORA..[SCHEMA1].[ORDERS] where doc_id = 'N2324JKL3511'
causes SQL Server to not send the WHERE clause and the query never completes.
I tried to use OPENQUERY however it won't let me add the doc_id to concatenate into the WHERE clause of the query string.
Then I tried to build a select FROM OPENQUERY string in a function but I can't use sp_executesql in a function to run it.
Any help is greatly appreciated.
I think this would logically work for you, but it may take too long as well.
SELECT sql_ord.*
FROM my_order_table sql_ord
LEFT JOIN LINK_ORA..[SCHEMA1].[ORDERS] ora_ord ON sql_ord.document_id = ora_ord.doc_id
WHERE sql_ord.document_id = 'N2324JKL3511'
AND ora_ord.doc_id IS NULL
Since you have problem with something as simple as select 1 from LINK_ORA..[SCHEMA1].[ORDERS] where doc_id = 'N2324JKL3511' have you try to create a table on the remote server that will hold the doc_id that you want to look at. So your SELECT will include a table that contain only 1 row. I'm just not sure about the INSERT since I can't test it for now. I'm assuming that everything will be done on the remote server.
So something like :
CREATE TABLE LINK_ORA..[SCHEMA1].linked_server_doc_id (
doc_id nvarchar(12));
INSERT INTO LINK_ORA..[SCHEMA1].linked_server_doc_id (doc_id)
SELECT doc_id
FROM LINK_ORA..[SCHEMA1].[ORDERS] WHERE doc_id = 'N2324JKL3511';
select 1
from my_order_table ord
where not exists (select 1
from LINK_ORA..[SCHEMA1].[linked_server_doc_id]
where doc_id = ord.document_id)
and document_id = 'N2324JKL3511';
DROP TABLE LINK_ORA..[SCHEMA1].linked_server_doc_id

sql temporary tables in rstudio notebook's sql chunks?

I am trying to use temp tables in an sql codechunk in rstudio.
An example: When I select one table and return it into an r object things seem to be working:
```{sql , output.var="x", connection='db' }
SELECT count(*) n
FROM origindb
```
When I try anything with temp tables it seems like the commands are running but returns an empty r data.frame
```{sql , output.var="x", connection='db' }
SELECT count(*) n
INTO #whatever
FROM origindb
SELECT *
FROM #whatever
```
My impression is that the Rstudio notebook sql chunks are just set to make one single query. So my temporary solution is to create the tables in a stored procedure in the database. Then I can get the results I want with something simple. I would prefer to have a bit more flexibility in the sql code chunks.
my db connection looks like this:
```{r,echo=F}
db <- DBI::dbConnect(odbc::odbc(),
driver = "SQL Server",
server = 'sql',
database = 'databasename')
```
Like this question, it will work if you put
set nocount on
at the top of your chunk. R seems to get confused when it's handed back the rowcount for the temp table.
I accomplished my goal using CTEs. As long as you define your CTEs in the order that they will be used it works. It is just like using temp tables with one big exception. The CTEs are gone after the query finishes where temp tables exist until you spid is kill (typically via a disconnect).
WITH CTE_WHATEVER AS (
SELECT COUNT(*) n
FROM origindb
)
SELECT *
FROM CTE_WHATEVER
You can also do this for multiple temp table examples
WITH CTE1 AS (
SELECT
STATE
,COUNTY
,COUNT(*) n
FROM origindb
GROUP BY
STATE
,COUNTY
),
CTE2 AS (
SELECT
STATE
,AVG(n)
,COUNTY_AVG
FROM CTE1
GROUP BY
STATE
)
SELECT *
FROM CTE2
WHERE COUNTY_AVG > 1000000
Sorry for the formatting. I couldn't figure out how to get the carriage returns to work in the code block.
I hope this helps.
You could manage a transaction within the SQL chunk defining a BEGIN and COMMIT clauses. For example:
BEGIN ;
CREATE TABLE foo (id varchar) ;
COMMENT ON TABLE foo IS 'Foo';
COMMIT ;

Openquery statement in SQL Server

I am fairly new to SQL, and I am hoping someone can help me with a problem I'm having. I haven't been able to find any answers helping me figure out this exact problem.
I have two tables in two SQL Server databases on two different servers that I want to compare using the column ItemID. I want to find records from Table1 that have an ItemID that does not exist in Table2 and insert those into a table variable. I have the following code:
--Create table variable to hold query results
DECLARE #ItemIDTable TABLE
(
[itemid][NVARCHAR](20) NULL
);
--Query data and insert results into table variable
INSERT INTO #ItemIDTable
([itemid])
SELECT a.[itemid]
FROM database1.dbo.table1 a
WHERE NOT EXISTS (SELECT 1
FROM [Database2].[dbo].[table2]
WHERE a.itemid = [Database2].[dbo].[table2].[itemid])
ORDER BY itemid
This works on a test server where the two databases are on the same server, but not in real life where they are on different servers. I tried the following using OPENQUERY, but I know I haven't got it quite right.
--Create table variable to hold query results
DECLARE #ItemIDTable TABLE
(
[ItemID][nvarchar](20) NULL
);
--Query data and insert results into table variable
INSERT INTO #ItemIDTable
([ItemID])
SELECT a.[ItemID]
FROM Database1.dbo.Table1 a
WHERE NOT EXISTS (SELECT 1
FROM OPENQUERY([Server2], SELECT * FROM [Database2].[dbo].[Table2]')
WHERE a.ItemID = [Database2].[dbo].[Table2].[ItemID])
ORDER BY ItemID
I'm pretty sure I need to do something in the WHERE clause, where I have the two databases on two servers, I'm just not quite sure how to structure it. Could anyone help?
You can't create an OPENQUERY that is correlated to an outer query. You could populate a temp table with the results of an OPENQUERY and do your WHERE NOT EXISTS against the temp table, or you might want to look into Synonyms.
Openquery works like this:
select *
from openquery
(LINKED_SERVER_NAME,
'select query goes here'
)
Note that the sql portion is single quoted. That means you might have to quote the quotes if necessary. For example:
select *
from openquery
(LINKED_SERVER_NAME,
'
select SomeTextField
from SomeTable
where SomeDateField = ''20141014''
'
)

Check if a list of items already exists in a SQL database

I want to create a group of users only if the same group does not exist already in the database.
I have a GroupUser table with three columns: a primary key, a GroupId, and a UserId. A group of users is described as several lines in this table sharing a same GroupId.
Given a list of UserId, I would like to find a matching GroupId, if it exists.
What is the most efficient way to do that in SQL?
Let say your UserId list is stored in a table called 'MyUserIDList', the following query will efficiently return the list of GroupId containing exactly your user list. (SQL Server Syntax)
Select GroupId
From (
Select GroupId
, count(*) as GroupMemberCount
, Sum(case when MyUserIDList.UserID is null then 0 else 1 End) as GroupMemberCountInMyList
from GroupUser
left outer join MyUserIDList on GroupUser.UserID=MyUserIDList.UserID
group by GroupId
) As MySubQuery
Where GroupMemberCount=GroupMemberCountInMyList
There are couple of ways of doing this. This answer is for sql server only (as you have not mentioned it in your tags)
Pass the list of userids in comma seperated to a stored procedure and in the SP create a dynamic query with this and use the EXEC command to execute the query. This link will guide you in this regard
Use a table-valued parameter in a SP. This is applicable to sql server 2008 and higher only.
The following link will help you get started.
http://www.codeproject.com/Articles/113458/TSQL-Passing-array-list-set-to-stored-procedure-MS
Hope this helps.
One other solution is that you convert the input list into a table. This can be done with various approaches. Unions, temporary tables and others. A neat solution combines the answer of
user1461607 for another question here on SO, using a comma-separated string.
WITH split(word, csv) AS (
-- 'initial query' (see SQLite docs linked above)
SELECT
'', -- place holder for each word we are looking for
'Auto,A,1234444,' -- items you are looking for
-- make sure the list ends with a comma !!
UNION ALL SELECT
substr(csv, 0, instr(csv, ',')), -- each word contains text up to next ','
substr(csv, instr(csv, ',') + 1) -- next recursion parses csv after this ','
FROM split -- recurse
WHERE csv != '' -- break recursion once no more csv words exist
) SELECT word, exisiting_data
FROM split s
-- now join the key you want to check for existence!
-- for demonstration purpose, I use an outer join
LEFT OUTER JOIN (select 'A' as exisiting_data) as t on t.exisiting_data = s.word
WHERE s.word != '' -- make sure we clamp the empty strings from the split function
;
Results in:
Auto,null
A,A
1234444,null

Sql Server CE can I delete TOP or only 1 record from table that matches my query

select Top(1)* from TableName Where columnName=value
selects only the first row just fine. However if I change the select to a delete I get an error and can't figure out how to write a query to delete only 1 record that matches my query from the db.
I'm wondering if anybody out there smarter than I knows if or how this can be done in SQL CE.
Did you try something like this?
DELETE TableName where IdColumn In ( select Top(1) IdColumn from TableName Where columnName=valuev)
I don't know specifically as I'm at home, but SQL CE is deliberately restricted in what it can do. One reason for this is that it is 'always' running locally to the process referencing it.
What means is that it is Expected that the other process is expected to handle much of the logic that may otherwise be encapsulated in the SQL Server. This often results in firing several queries at the SQL CE instance, where you may be more accustomed to firing off one.
In this case, you could do it with two queries...
1) A query to identify the record that you want to delete
2) Use that Identifier in another query to do the actual delete
You could also try using SET ROWCOUNT 1 to limit the DELETE to just 1 row. But again, I don't know if that works in CE.
You can use CTE such as
;with myTopRow(rowID)
(
select Top 1 rowID from TableName Where columnName=value
)
delete from TableName inner join myTopRow on TableName.rowID = myTopRow.rowID
Shouldn't it be rather :
DELETE FROM TableName WHERE columnName=value ORDER BY columnName LIMIT 1;
IMHO, the table logically has NO order by itself. It has it physically, but you can't rely on it. So, you HAVE to set the order in which you want to delete the first row.
The following code will delete only first row
Dim mySqlCommondDelete As String = "DELETE BOOK_ID, MemberID FROM (SELECT TOP 1 * FROM ISSUE_BOOK) where BOOK_ID = Val(" & deleteBook & ") and MemberID = Val(" & msk & ")"