Join array of numbers with table in function - arrays

I need some support with array type, because it is a new thing for me, so
I have a function:
create or replace type num_array as table of number;
create or replace function functionname(arr_in num_array)
return num_array is
tab num_array;
begin
select id_acc bulk collect into tab from (
SELECT a.id_acc
FROM (SELECT id_acc, parent_acc FROM account) a
connect by nocycle prior a.id_acc=a.parent_acc
start with id_acc in
(
select distinct ID_ACC
from (SELECT id_acc, parent_acc FROM account
) a
where parent_acc = id_acc
connect by nocycle prior a.parent_acc = a.id_acc or parent_acc is null
start with id_acc in (select parent_acc from table_name t,account a where t.id=a.id_acc)));
return tab;
end;
As an input I want to have an array of numbers (id). I want to connect that number (from input) with account table. It is in line:
start with id_acc in (select parent_acc from table_name t,account a where t.id=a.id_acc)));
I would like to join somehow table account with numbers from input,
I was trying to use table(tab()),account a but I got an error.
As output I would like to have result of select query so (return tab).

I'm not sure I understood what you want to achieve. Just to help you with the syntax, look at this:
start with id_acc in (select * from table(arr_in));

Below i have provided a small snippet which basically illustrates your issue with joining Nested Table type with Table.
CREATE OR REPLACE FUNCTION test_ntt_join
RETURN NUMBER_NTT
AS
lv_tab_num NUMBER_NTT;
lv_tab2 NUMBER_NTT;
BEGIN
SELECT LEVEL BULK COLLECT INTO lv_tab_num FROM DUAL CONNECT BY LEVEL < 10;
SELECT COLUMN_VALUE
BULK COLLECT INTO
lv_tab2
FROM TABLE(lv_tab_num) t,
EMP
WHERE emp.empno= t.column_value;
RETURN lv_tab2;
END;
------------------------------------------OUTPUT------------------------------------
select * from table(test_ntt_join);
COLUMN_VALUE
1
1
3
------------------------------------------OUTPUT------------------------------------

Related

Select IDs which belongs ONLY to the list passed as parameter

Let's start from data:
DECLARE #Avengers TABLE ([Hero] varchar(32), [Preference] varchar(32));
INSERT INTO #Avengers VALUES
('Captain_America','gingers'),('Captain_America','blondes'),
('Captain_America','brunettes'),('Hulk','gingers'),('Hulk','blondes'),
('Hawkeye','gingers'),('Hawkeye','brunettes'),('Iron_Man','blondes'),
('Iron_Man','brunettes'),('Thor','gingers'),('Nick_Fury','blondes');
Now I would like to pass a #Preferences as a list of [Preference] (either comma separated or single column table parameter) without knowing how many parameters I am going to get and based on this to select [Hero] who prefers exactly these #Preferences as provided in parameter (list), by that I mean if I am after 'blondes' and 'gingers' then I am after 'Hulk' only
(NOT 'Captain_America' who prefers 'blondes', 'gingers' and 'brunettes').
I would like to get something like:
SELECT [Hero]
FROM #Avengers
WHERE *IS_ASSIGNED_ONLY_TO_THE_LIST*([Preference]) = #Preference
Well, I think I overcomplicated my code, but it works.
SELECT a.Hero, COUNT(*), MIN(p.N)
FROM #Avengers a
LEFT JOIN ( SELECT *, COUNT(*) OVER() N
FROM #Preferences) p
ON a.Preference = p.Preference
GROUP BY a.Hero
HAVING COUNT(*) = MIN(p.N)
AND COUNT(*) = COUNT(p.Preference)
;
I'm using #Preferences as a table.

MS SQL If Selected is NULL Run another statement

I have two sql statements,
select * from UserTable where role='HOD'
select * from UserTable where role='Supervisor'
I want the results to be in a way such that if the first statement returns nothing, I want the second statement to run and if first statement returns something, second statement do not need to run. Is there a way to do it, be it in a stored procedure or a SQLQuery?
Executing two queries is more expensive than executing a single one that returns both result sets. It would be cheaper to filter the results on the client.
Even if you return all results in a single query, you can differentiate the two cases. For example, HOD always comes before Supervisor. You could use a ranking function like ROW_NUMBER() to assign a value to each row, depending on whether it matches HOD or Supervisor:
with a as (
select * ,row_number() over (partition by Role order by Role) as rn
from UserTable
where Role in ('HOD','Supervisor')
)
select *
from a
where rn=1
Another option is to combine a query that returns HOD with a query that returns Supervisor if HOD doesn't exist:
select *
from UserTable
where Role ='HOD'
UNION ALL
select *
from UserTable
where Role ='Supervisor'
AND NOT EXISTS (SELECT 1
from UserTable
where Role ='Supervisor')
The performance of both queries can be improved if Role is part of an index. The first query becomes equivalent to a simple index seek if the table has an index that covers all the returned fields. If the query returns only eg, ID, UserName, Role :
with a as (
select * ,row_number() over (partition by Role order by Role) as rn
from UserTable
where Role in ('HOD','Supervisor')
)
select ID,UserName, Role
from a
where rn=1
and the table has a covering index:
CREATE INDEX IX_UserTable_Roles ON UserTable (Role,ID,UserName)
The resulting execution plan is a single INDEX SEEK on the index
Try This
IF EXISTS (
SELECT 1 FROM UserTable WHERE ROLE = 'HOD'
)
BEGIN
SELECT * FROM UserTable WHERE ROLE = 'HOD'
END
ELSE BEGIN
SELECT * FROM UserTable WHERE ROLE = 'Supervisor'
END
This should do :
IF EXISTS(SELECT 1 FRom UserTable where role='HOD')
Begin
select * from UserTable where role='HOD'
END
ELSE BEGIN
select * from UserTable where role='Supervisor'
END
If you are using Transact SQL then a good page to read about this is https://learn.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql
In the Transact SQL instance, the general version of what you are looking for is;
IF Boolean_expression
{ sql_statement | statement_block }
[ ELSE
{ sql_statement | statement_block } ]

Loop through SQL Server Select staement results and use them for another SELECT query

I am trying to run a basic select statement to return a list of job numbers. I want to then take that list of job numbers and crank them through 4 different select statement to see if I get any results. The point of this is to create a watchdog list to notify me if any parts from any jobs have not been created. Here is my code thus far:
--Search for all Jobs
--Use select statement here to return a list of job numbers
--Then crank through both of the following queries to see if I get any results
--Laser 1
SELECT * FROM db1.dbo.table1
WHERE PARTNAME NOT IN (SELECT PARTNAME FROM db2.Laser1.dbo.Part WHERE ORDERNO = #JobNumber)
AND DISTRICT = 1 AND ORDERNO = #JobNumber
--Laser 2
SELECT * FROM db1.dbo.table1
WHERE PARTNAME NOT IN (SELECT PARTNAME FROM db2.Laser2.dbo.Part WHERE ORDERNO = #JobNumber)
AND DISTRICT = 3 AND ORDERNO = #JobNumber
In short I want to use a select statement to return all jobs that are currently in proccess. Then replace the #JobNumber with that job and see if it returns anything.
Pretty sparse on details here but you definitely don't need a cursor or a loop for this. Something like this perhaps?
select *
from Jobs j
where j.PARTNAME not in
(
SELECT p1.PARTNAME
from db1.dbo.table1
left join db2.Laser1.dbo.Part p1 on p1.ORDERNO = #JobNumber AND DISTRICT = 1
UNION ALL
SELECT p2.PARTNAME
from db1.dbo.table1
left join db2.Laser2.dbo.Part p2 on p2.ORDERNO = #JobNumber AND DISTRICT = 3
)

Is there a way to loop through recordset and identify different values?

I have a MS SQL stored Procedure that loops the records of a select statement using a cursor. When I loop and read each record it could be 3 then 3 then 3 or it could be 3 then 4 and then 5 then 3. It could be one record or more than one.
What I need to know is are all of the values the same or are they different? I don't care what they are only if they are the same or different. If I was doing this with client side code I would use a list and see if that value is in the list then when I'm finished I would count the values if one then the same if more than 1 then different.
DECLARE TC CURSOR LOCAL
FOR SELECT ProgramID FROM tblPublications
OPEN TC
FETCH NEXT FROM TC INTO #ProgramID
WHILE ##FETCH_STATUS = 0
BEGIN
Print #ProgramID
--Same or Different then the last one???
FETCH NEXT FROM TC2 INTO #ProgramID
END
CLOSE TC
DEALLOCATE TC
END
If they are the same then I will update a record one way and if they are different then update a status record another way.
Thanks
Something like this (no cursor involved) perhaps?
select PubId, count(distinct ProgId)
from tblPublications
group by PubId
You can use count(distinct col) to get the number of distinct values for a column, and use a case expression to return a value depending on if it is 1 or more.
select
PublicationId
, PublicationType = case
when count(distinct ProgramId) > 1
then 'inter'
else 'intra'
end
from tblPublications
where PublicationId = 12345
group by PublicationId
you can compare the minimum and maximum of ProgramID in a group.
If all the value is same, the max value will be same as min value.
;with tblPublications(ProgramID,ID)AS(
select 'a',1 union
select 'b',1 union
select 'c',2 union
select 'c',2
)
select case when ISNULL(max(ProgramID),'')=ISNULL(min(ProgramID),'') then 'Same' else 'Different' end
from tblPublications
group by ID
(No column name)
1 Different
2 Same

SQL Server - Insert into with select and union - duplicates being inserted

When I execute my "select union select", I get the correct number or rows (156)
Executed independently, select #1 returns 65 rows and select #2 returns 138 rows.
When I use this "select union select" with an Insert into, I get 203 rows (65+138) with duplicates.
I would like to know if it is my code structure that is causing this issue ?
INSERT INTO dpapm_MediaObjectValidation (mediaobject_id, username, checked_date, expiration_date, notified)
(SELECT FKMediaObjectId, CreatedBy,#checkdate,dateadd(ww,2,#checkdate),0
FROM dbo.gs_MediaObjectMetadata
LEFT JOIN gs_MediaObject mo
ON gs_MediaObjectMetadata.FKMediaObjectId = mo.MediaObjectId
WHERE UPPER([Description]) IN ('CAPTION','TITLE','AUTHOR','DATE PHOTO TAKEN','KEYWORDS')
AND FKMediaObjectId >=
(SELECT TOP 1 MediaObjectId
FROM dbo.gs_MediaObject
WHERE DateAdded > #lastcheck
ORDER BY MediaObjectId)
GROUP BY FKMediaObjectId, CreatedBy
HAVING count(*) < 5
UNION
SELECT FKMediaObjectId, CreatedBy,getdate(),dateadd(ww,2,getdate()),0
FROM gs_MediaObjectMetadata yt
LEFT JOIN gs_MediaObject mo
ON yt.FKMediaObjectId = mo.MediaObjectId
WHERE UPPER([Description]) = 'KEYWORDS'
AND FKMediaObjectId >=
(SELECT TOP 1 MediaObjectId
FROM dbo.gs_MediaObject
WHERE DateAdded > #lastcheck
ORDER BY MediaObjectId)
AND NOT EXISTS
(
SELECT *
FROM dbo.fnSplit(Replace(yt.Value, '''', ''''''), ',') split
WHERE split.item in (SELECT KeywordEn FROM gs_Keywords) or split.item in (SELECT KeywordFr FROM gs_Keywords)
)
)
I would appreciate any clues into resolving this problem ...
Thank you !
The UNION keyword should only return distinct records between the two queries. However, if I recall correctly, this is only true if the datatypes are the same. The date variables might be throwing that off. Depending on the collation type, whitespace might be handled differently as well. You might want to do a SELECT DISTINCT on the dpapm_MediaObjectValidation table after doing your insert, but be sure to trim whitespace from both sides in your comparison. Another approach is to do your first insert, then on your second insert, forgo the UNION altogether and do a manual EXISTS check to see if the items to be inserted already exist.

Resources