stuck in trying to use uiee in mssql - sql

I'm not new to programming but new to mssql. I have searched online for help with this but am getting nowhere.
I have a book inventory file in UIEE format.
UR|2706
AA|Parker, William Harwar
TI|RECOLLECTIONS OF A NAVAL OFFICER, 1841-1865:
XD|S
UR|15184
AA|Goodrich, Norma Lorre
TI|King Arthur
and so on. As you see each line is tagged and each record starts with a 'UR' tag and ends with a 'XD' tag. I've uploaded this to a mssql table called testuiee with columns tag and data. I'm trying to use sql to identify the fields and put them into another table called btdata. For example, put UR data into Book id, AA into author, etc.
Book_id Author Title
2706 Parker, William Harwar RECOLLECTIONS OF A NAVAL OFFICER, 1841-1865:
15184 Goodrich, Norma Lorre King Arthur
The script I wrote works but only puts the last record into the btdata table. I've tried conditional processing, (while, if, case) and also a cursor but the result is always one record, the last one. King Arthur in this example. I'm sure its because I am thinking of things in a row by row way and not in blocks of data but I can't get my head around this.
Here's my code so far.
DECLARE #bookid nvarchar(max),
#author nvarchar(max),
#title nvarchar(max)
SELECT tag, data from testuiee
SELECT #bookid = data from testuiee where tag = 'UR'
SELECT #author = data from testuiee where tag = 'AA'
SELECT #title = data from testuiee where tag = 'TI'
IF #bookid IS NOT NULL
INSERT INTO btdata (book_id, author, title)
VALUES (#bookid, #author, #title)
GO
I did try to use the 'XD' tag conditionally to execute the insert but the same result.
Any help would be greatly appreciated.

You will need an Auto ID field/Rownumber field in your table to be able to keep the Rows together.
With you script you will get "random" values fitting your condition.
Using ROW_NUMBER() OVER (Partition by tag Order By ID) you are able to join your CTE with 3 aliases for the desired result.
Using SQL Server 2008 + you might use a CTE
declare #testui Table (ID integer Identity(1,1),tag varchar(10),data varchar(50))
declare #dest Table (book_id varchar(50),author varchar(50),title varchar(50))
Insert into #Testui
Select 'UR','2706'
Insert into #Testui
Select 'AA','Parker, William Harwar'
Insert into #Testui
Select 'TI','RECOLLECTIONS OF A NAVAL OFFICER, 1841-1865:'
Insert into #Testui
Select 'XD','S'
Insert into #Testui
Select 'UR','15184'
Insert into #Testui
Select 'AA','Goodrich, Norma Lorre'
Insert into #Testui
Select 'TI','King Arthur'
;With CTE as (
Select *,ROW_NUMBER() OVER (Partition by tag Order By ID) as RN
from #Testui)
Insert into #dest
Select a1.Data as book_id,a2.data author,a3.data title
from CTE a1
JOIN CTE a2 ON a1.RN=A2.RN
JOIN CTE a3 ON a1.RN=A3.RN
Where a1.tag='UR' and a2.tag='AA' and a3.tag='TI'
Select * from #dest

Related

Is it possible to get the last number generated from ROW_NUMBER in SQL

I currently have such a query inside my stored procedure:
INSERT INTO YTDTRNI (TRCDE, PROD, WH, DESCR, UCOST, TCOST, DRAC, CRAC, REM, QTY, UM, ORDNO, TRDATE, SYSDATE, PERIOD, USERID)
SELECT
'AJ', PROD, WH, DESCR, 0, -TCOST, STKGL, COSGL,
'MASS ADJUSTMENT', 0, UM,
CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS nvarchar(255)),
GETDATE(), GETDATE(), #inputPeriod, #inputUserId
FROM
INV
WHERE
H = 0
I am making use of row_number() to get a number that is incrementing itself while executing the query.
For example the query above INSERT 2018 records in YTDTRNI table. So the last number generated by this row_number() function is 2018. My question now is whether is it possible to get hold of this last number generated by row_number().
In another table, I have a value stored as I1000 for example. So after performing the above operation. I need to update this table with the new value of I3018 (1000+2018).
I am stuck on how to move on. Open to any advice if whatever I am doing is incorrect or not following conventions/standards.
just do a ##rowcount after your query
DECLARE #rc INT
INSERT INTO YTDTRNI ( ... )
SELECT #rc = ##rowcount
after that you can use this #rc to update the other table
##ROWCOUNT is not reliable if there are triggers in the database. I would strongly discourage you from using it.
Instead, use OUTPUT:
declare #t table (rn int);
insert into . . .
output (inserted.ordno) into #t
select . . .;
Then you can simply do:
select max(ordno) from #t;
This captures exactly what is input into the table.

Dynamic SQL - union all tables (number of tables is dynamically created)

I don't know how to union all tables with dynamic SQL.
The issue is that I'm inserting into db a number of tables - all having the same structure (only one varchar column
[Line]
). I don't know that would be the number of tables inserted - it depends on the project. But I want to automate the process in SQL.
I'm using this query to find those tables, additionally I'm adding some [RowNum] that may serve as an ID of each table:
SELECT
ROW_NUMBER() OVER (ORDER BY Name) AS [RowNum],
[Name] AS [Name]
INTO #all_tables_with_ids
FROM #all_tables
This query returns:
RowNum | Name
------------------------
1 | Table 1
2 | Table 2
3 | Table 3
4 | Table 4
I would like to merge all tables together. I was trying to write some insert into in while loop but it didn't work. I figured out that I need dynamic SQL.
Can you suggest something? I was trying to find some examples but all of them fail due to the fact that the list of tables is not known at the beginning, so it needs to be created dynamically as well.
Demo here:
create table #test
(
RowNum int,
Name varchar(100)
)
insert into #test
select 1,quotename('table1')
union all
select 2,quotename('table2')
declare #sql nvarchar(max)
set #sql='select somecol from tbl union all '
declare #sql1 nvarchar(max)
;with cte
as
(select #sql as ql,name,rplc
from
#test t1
cross apply
(select replace(#sql,'tbl',name) as rplc from #test t2 where t1.rownum=t2.rownum)b
)
select #sql1= stuff(
(select ''+rplc
from cte
for xml path('')
),1,0,'')
set #sql1=substring(#sql1,1,len(#sql1)-10)
print #sql1
--exec(#Sql1)

Sql Developer - Can you use a case statement in a cursor to return multiple values

I've been working through a task of trying to classify several million rows of data into a variety of different topics. The data involves calls from our customer support, and we're trying to find a way to classify each call into one of 109 topics. Due to the confidentiality of the data I can't disclose any of the actual data, but will try to give a relatable subset of data that other people could compare to.
DATA:
Incident_Number | Call_Description
000123456 | Issue with oranges and apples
000987654 | oranges
004567891 | with apples and kiwis
026589741 | Issue with kiwis
SQL:
select
Incident_Number,
Call_Description,
(case
when call_description like '%oranges%' then oranges
when call_description like '%apples%' then apples
when call_descritpion like '%kiwis%' then 'kiwis'
else 'Unclassified' end) Topic
from DATA
Question
My hope would be to have Incident 000123456 classified as both oranges and apples and Incident 004567891 get classified as apples and kiwis
Desired Output
Incident_Number | Call_Description ......................| Topic
000123456 ........ | Issue with oranges and apples | oranges
000123456 ........ | Issue with oranges and apples | apples
000987654 ........ | oranges ...................................| oranges
004567891 .........| with apples and kiwis............... | apples
004567891 .........| with apples and kiwis............... | kiwis
026589741 .........| Issue with kiwis........................ | kiwis
Wrapup
From my limited knowledge and what I've garnered from research a simple case statement can't do this because it short circuits after finding the first true value. My question is whether or not it is possible to make some alterations to my code OR instead to somehow set up a cursor to run through my initial table and give me the desired output noted above.
I appreciate any help or advice and hope that I've adhered to the rules of this website (which has honestly saved my butt before!)
Regards,
Richard
I use Microsoft SQL Server instead of Oracle, so I'm not sure about the Oracle syntax, but one solution I have used in the past is to create a temporary table:
CREATE GLOBAL TEMPORARY TABLE my_temp_table (
groupName varchar(50)
) ON COMMIT DELETE ROWS;
Insert Into my_temp_table (groupName) VALUES('oranges')
Insert Into my_temp_table (groupName) VALUES('apples')
Insert Into my_temp_table (groupName) VALUES('kiwis')
then I would inner join to the table to duplicate the records:
select
Incident_Number,
Call_Description,
my_temp_table.groupName Topic
from DATA
inner join my_temp_table
on Data.call_description like '%' + my_temp_table.groupName + '%'
One problem with this method is that if a record doesn't fall into any categories, it will be excluded completely.
One option would be to create (physically or virtually) a table of keywords and use that to join to your table of data. Something like
WITH keywords AS (
SELECT 'apples' topic FROM dual UNION
SELECT 'oranges' FROM dual UNION
SELECT 'kiwis' FROM dual
)
SELECT data.incident_number,
data.incident_description,
keywords.topic
FROM data
JOIN keywords
ON( data.incident_description LIKE '%' || keywords.topic || '%' )
This will work but it's not the most efficient or flexible approach in the world. It doesn't handle different forms of the word well (if a description references the singular "apple" for example). It doesn't handle words that appear within other words (if a description talks about "crabapples" for example). And it's doing a relatively slow match based on scanning through the entire incident_description.
An alternative approach would be to use Oracle Text to index your data. That's likely to be a more complex solution but it would be much more flexible and should be more efficient.
My exaple is for MS SQL Server: http://www.sqlfiddle.com/#!3/8e904/2
You could create a table with details and a simple cross join with it
create table data(
Incident_Number varchar(9),
Call_Description varchar(50))
create table detail(detail varchar(20))
insert into data select
'000123456','Issue with oranges and apples'
union select
'000987654', 'oranges '
union select
'004567891', 'with apples and kiwis'
union select
'026589741', 'Issue with kiwis'
insert into detail select 'kiwis'
union select 'oranges'
union select 'apples'
select * from data a
cross join detail b
where a.call_description like '%'||b.detail||'%'
order by a.Incident_Number
Add a new version http://www.sqlfiddle.com/#!3/b3378/1
It manages all incidents identifying those without coincidences with null
select * from data a
left join detail b
on a.call_description like '%'||b.detail||'%'
I have gone for the CURSOR option to loop through the records in the table and update each row with the category information.
Because you mentioned your rules are quite complicated I think each one might need to be hand written in this procedure and not stored in a table.
Sorry this is MSSQL so might need converting a little for Oracle.
/*BEGIN SETUP TEMP DATA*/
CREATE TABLE #tmp1 (
idno int,
title nvarchar(100),
Categories nvarchar(100) --I have added this row to my temp data, you could store this in another table if needed.
)
INSERT INTO #tmp1 (idno, title) VALUES (1, 'problem apples and oranges')
INSERT INTO #tmp1 (idno, title) VALUES (2, 'problem with apples')
INSERT INTO #tmp1 (idno, title) VALUES (3, 'problem with oranges')
INSERT INTO #tmp1 (idno, title) VALUES (4, 'problem with kiwis')
INSERT INTO #tmp1 (idno, title) VALUES (5, 'problem with something')
/*END SETUP TEMP DATA*/
/*SETUP VARIABLES TO USE IN CURSOR*/
DECLARE #idno int,
#title nvarchar(100)
/*DECLARE CURSOR, OPEND IT AND FETCH DATA INTO INTO IT*/
DECLARE incident_cursor CURSOR FOR
SELECT idno, title
FROM #tmp1 --You could add WHERE Categories IS NULL to only update records that have not been processed
OPEN incident_cursor
FETCH NEXT FROM incident_cursor
INTO #idno, #title
/*LOOP THROUGH CURSOR*/
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #allCategories nvarchar(100)
SET #allCategories = ''
/*WRITE RULES HERE TO WORK OUT WHETHER CATEGORY NEEDS ADDING*/
IF (#title LIKE '%apples%') BEGIN SET #allCategories = #allCategories + 'Apples ' END
IF (#title LIKE '%oranges%') BEGIN SET #allCategories = #allCategories + 'Oranges ' END
IF (#title LIKE '%kiwis%') BEGIN SET #allCategories = #allCategories + 'Kiwis ' END
IF #allCategories = '' BEGIN SET #allCategories = 'Uncategorised' END
/*UPDATE ORIGINAL TABLE WITH CATEGORY INFORMATION*/
UPDATE #tmp1 SET Categories = #allCategories WHERE idno = #idno
FETCH NEXT FROM incident_cursor
INTO #idno, #title
END
CLOSE incident_cursor;
DEALLOCATE incident_cursor;
/*THIS ARE JUST TO DISPLAY OUTPUT AND CLEAR UP TEST DATA*/
SELECT * FROM #tmp1
DROP TABLE #tmp1
You could set this to run in a scheduled job to update your records as frequently as you see fit.
Might not be a perfect solution for you but hopefully a starting point
Sorry for how long it's taken me to come back to this. I got pulled into some more pressing deliverables. I ended up figuring out how to get it working by storing the queries in one table as records, then running them using an execute immediate command. I've included the code below in case someone can use it in the future.
create table text_kw_search_queries
(
incident_number varchar2(15),
description varchar2(100),
topic_level_1 varchar2(89),
topic_level_2 varchar2(89)
);
DECLARE
V_SQL VARCHAR2(1000);
CURSOR UPTO IS
SELECT TOPIC_level_1, TOPIC_level_2, description FROM tcstopic
;
BEGIN
FOR i IN UPTO LOOP
V_SQL := 'INSERT /*+APPEND PARALLEL(TEXT_KW_SEARCH_LOOP,2)*/ INTO text_kw_search_queries
SELECT
t2.Incident_Number,
t2.description,
t2.TOPIC_level_1,
t2.TOPIC_level_2
FROM (
SELECT
t1.Incident_number,
t1.description,
'''||i.TOPIC_level_1||''' AS TOPIC_level_1,
'''||i.TOPIC_level_2||''' AS TOPIC_level_2,
(CASE WHEN '|| i.description ||' THEN 1 ELSE 0 END) as FLAG
FROM cso_text_query t1
) t2
WHERE t2.FLAG = 1';
EXECUTE IMMEDIATE V_SQL;
DBMS_OUTPUT.PUT_LINE(V_SQL);
COMMIT WORK;
END LOOP;
END;

find substrings in sql table using sql query

I have a column of "name" in my sql table. In my sql query i want to fetch all the records where column "name" is substring of my input string.
For exapmle, user enters "My name is Davivd", then I want to fetch all the records where name is David.
P.S: User may enters something like this "Its David here".
Anyone who knows please let me know. Thanku
A simple view of this would be:
DECLARE #x VARCHAR(255)
SET #x = 'My name is David'
SELECT a FROM tablex WHERE #x LIKE '%' + tablex.name + '%'
This reverses #shortspider's response.
Your question is a bit unclear, but if you have a name column you can find it as part of any string using the CHARINDEX function:
Example:
DECLARE #TABLE TABLE (ID INT IDENTITY(1,1), NAME VARCHAR(100))
INSERT INTO #TABLE(NAME)
SELECT 'DAVID' UNION ALL
SELECT 'GOLIATH' UNION ALL
SELECT 'DAVE' UNION ALL
SELECT 'MARTIN'
SELECT *
FROM #TABLE
WHERE CHARINDEX(NAME,'DID YOU EVER READ THE STORY ABOUT DAVID AND GOLIATH?') > 0
SELECT *
FROM #TABLE
WHERE CHARINDEX(NAME,'MY FAVOURITE MOVIE DIRECTOR IS MARTIN SCORCESE. I LOVE HIS GLASSES.') > 0
Try: SELECT * FROM table_name WHERE name LIKE '%David%'

Is there a way to return multiple results with a subquery?

I have need to return multiple results from a subquery and have been unable to figure it out. The end result will produce the persons name across the vertical axis, various actions based on an action category across the horizontal axis. So the end result looking like:
----------
**NAME CATEGORY 1 CATEGORY 2**
Smith, John Action 1, Action 2 Action 1, Action 2, Action 3
----------
Is there a way to do this in a single query?
select
name,
(select action from actionitemtable where actioncategory = category1 and contact = contactid)
from
contact c
inner join actionitemtable a
on c.contactid = a.contactid
If more than one result is returned in that subquery I would like to be able to display it as a single comma separated string, or list of actions, etc.
Thank you.
Microsoft Sql Server 2005 is being used.
I use a User Defined Function for this task. The udf creates a delimited string with all elements matching the parameters, then you call the udf from your select statement such that you pull a delimited list for each record in the recordset.
CREATE FUNCTION dbo.ud_Concat(#actioncategory int, #contactid int)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #sOutput VARCHAR(8000)
SET #sOutput = ''
SELECT #sOutput = COALESCE(#sOutput, '') + action + ', '
FROM dbo.actionitemtable
WHERE actioncategory=#actioncategory AND contact=#contact
ORDER BY action
RETURN #sOutput
END
SELECT
name,
dbo.ud_Concat(category1, contactid) as contactList
FROM contact c
INNER JOIN actionitemtable a ON c.contactid = a.contactid
you need to give more info about your table structure and how they join to each other.
here is a generic example about combining multiple rows into a single column:
declare #table table (name varchar(30)
,ID int
,TaskID char(3)
,HoursAssigned int
)
insert into #table values ('John Smith' ,4592 ,'A01' ,40)
insert into #table values ('Matthew Jones',2863 ,'A01' ,20)
insert into #table values ('Jake Adams' ,1182 ,'A01' ,100)
insert into #table values ('Matthew Jones',2863 ,'A02' ,50)
insert into #table values ('Jake Adams' ,2863 ,'A02' ,10)
SELECT DISTINCT
t1.TaskID
,SUBSTRING(
replace(
replace(
(SELECT
t2.Name
FROM #Table AS t2
WHERE t1.TaskID=t2.TaskID
ORDER BY t2.Name
FOR XML PATH(''))
,'</NAME>','')
,'<NAME>',', ')
,3,2000) AS PeopleAssigned
FROM #table AS t1
OUTPUT:
TaskID PeopleAssigned
------ --------------------------------------
A01 Jake Adams, John Smith, Matthew Jones
A02 Jake Adams, Matthew Jones
(2 row(s) affected)
This is pretty abstract and complex. My initial reaction was "pivot query", but the more I looked at it (and at the earlier responses) the more I thought: Can you pass this one off to the application team? You return the "base", and they write and apply the procedural code that makes this kind of problem a snap. Sure, you can squeeze it in to SQL, but that doesn't make it the right place to do the work.
According to your query try this:
SELECT [Name],
STUFF(
(
SELECT ' ,' + [Action]
FROM [AactionItemTable]
WHERE [ActionCategory] = category1
AND [Contact] = contactid
FOR XML PATH('')
), 1, 2, ''
) AS [AdditionalData]
FROM [Contact] C
INNER JOIN [ActionItemTable] A
ON C.[ContactId] = A.[ContactId]
Guess this is the simplest way to do what you want.
EDIT: if there is no action in the subquery found, the [AdditionalData] result will be NULL.
You will probably have to create a custom aggregate function. Microsoft has a knowledge base article with sample code here.
If you don't mind using cursors, you can write your own function to do this. Here's an example that will work on the Adventureworks sample DB:
CREATE FUNCTION CommaFunctionSample
(
#SalesOrderID int
)
RETURNS varchar(max)
AS
BEGIN
DECLARE OrderDetailCursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT SalesOrderDetailID
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = #SalesOrderID
DECLARE #SalesOrderDetailID INT
OPEN OrderDetailCursor
FETCH NEXT FROM OrderDetailCursor INTO #SalesOrderDetailID
DECLARE #Buffer varchar(max)
WHILE ##FETCH_STATUS = 0
BEGIN
IF #Buffer IS NOT NULL SET #Buffer = #Buffer + ','
ELSE SET #Buffer = ''
SET #Buffer = #Buffer + CAST(#SalesOrderDetailID AS varchar(12))
FETCH NEXT FROM OrderDetailCursor INTO #SalesOrderDetailID
END
CLOSE OrderDetailCursor
DEALLOCATE OrderDetailCursor
RETURN #Buffer
END
This is how such a function would appear in a select query:
SELECT AccountNumber, dbo.CommaFunctionSample(SalesOrderID)
FROM Sales.SalesOrderHeader