Write a pl sql block to fetch distinct department ids - database

Write a pl sql block to fetch distinct department idsenter image description here

That would be something like this (read comments within code).
Though, the exercise is just the opposite of what I'd expect because - normally - departments exist, but nobody works in them. With enforced referential integrity constraint, you couldn't have situation as described in the exercise. After all, that's what your homework text suggests.
set serveroutput on
declare
l_dept_name dept.dept_name%type;
begin
-- loop through all distinct DEPT_ID values in the EMP table
for cur_r in (select distinct e.dept_id
from emp e
)
loop
-- starting inner BEGIN-EXCEPTION-END block so that
-- loop goes on in case of an error
begin
-- retrieve department details
select d.dept_name
into l_dept_name
from dept d
where d.dept_id = cur_r.dept_id;
-- display department info (because SELECT returned something for CUR_R.DEPT_ID)
dbms_output.put_line('Dept ID: ' || cur_r.dept_id || ' is ' || l_dept_name);
-- handle errors (because SELECT didn't return anything for CUR_R.DEPT_ID)
exception
when no_data_found then
dbms_output.put_line('Dept ID: ' || cur_r.dept_id ||': no additional info');
end;
end loop;
end;
/

Related

Can I use a variable inside cursor declaration?

Is this code valid?
-- Zadavatel Login ID
DECLARE #ZadavatelLoginId nvarchar(max) =
(SELECT TOP 1 LoginId
FROM
(SELECT Z.LoginId, z.Prijmeni, k.spojeni
FROM TabCisZam Z
LEFT JOIN TabKontakty K ON Z.ID = K.IDCisZam
WHERE druh IN (6,10)) t1
LEFT JOIN
(SELECT ko.Prijmeni, k.spojeni, ko.Cislo
FROM TabCisKOs KO
LEFT JOIN TabKontakty K ON K.IDCisKOs = KO.id
WHERE druh IN (6, 10)) t2 ON t1.spojeni = t2.spojeni
AND t1.Prijmeni = t2.Prijmeni
WHERE
t2.Cislo = (SELECT CisloKontOsoba
FROM TabKontaktJednani
WHERE id = #IdKJ))
-- Pokud je řešitelský tým prázdný
IF NOT EXISTS (SELECT * FROM TabKJUcastZam WHERE IDKJ = #IdKJ)
BEGIN
DECLARE ac_loginy CURSOR FAST_FORWARD LOCAL FOR
-- Zadavatel
SELECT #ZadavatelLoginId
END
ELSE BEGIN
I am trying to pass the variable #ZadavatelLoginId into the cursor declaration and SSMS keeps telling me there is a problem with the code even though it is working.
Msg 116, Level 16, State 1, Procedure et_TabKontaktJednani_ANAFRA_Tis_Notifikace, Line 575 [Batch Start Line 7]
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
Can anyone help?
I do not see anything in your posted query that could trigger the specific message that you listed. You might get an error if the subquery (SELECT CisloKontOsoba FROM TabKontaktJednani WHERE id = #IdKJ) returned more than one value, but that error would be a very specific "Subquery returned more than 1 value...".
However, as written, your cursor query is a single select of a scalar, which would never yield anything other than a single row.
If you need to iterate over multiple user IDs, but wish to separate your selection query from your cursor definition, what you likely need is a table variable than can hold multiple user IDs instead of a scalar variable.
Something like:
DECLARE #ZadavatelLoginIds TABLE (LoginId nvarchar(max))
INSERT #ZadavatelLoginIds
SELECT t1.LoginId
FROM ...
DECLARE ac_loginy CURSOR FAST_FORWARD LOCAL FOR
SELECT LoginId
FROM #ZadavatelLoginIds
OPEN ac_loginy
DECLARE #LoginId nvarchar(max)
FETCH NEXT FROM ac_loginy INTO #LoginId
WHILE ##FETCH_STATUS = 0
BEGIN
... Send email to #LoginId ...
FETCH NEXT FROM ac_loginy INTO #LoginId
END
CLOSE ac_loginy
DEALLOCATE ac_loginy
A #Temp table can also be used in place of the table variable with the same results, but the table variable is often more convenient to use.
As others have mentioned, I believe that your login selection query is overly complex. Although this was not the focus of your question, I still suggest that you attempt to simplify it.
An alternative might be something like:
SELECT Z.LoginId
FROM TabKontaktJednani KJ
JOIN TabCisKOs KO ON KO.Cislo = KJ.CisloKontOsoba
JOIN TabCisZam Z ON Z.Prijmeni = KO.Prijmeni
JOIN TabKontakty K ON K.IDCisZam = Z.ID
WHERE KJ.id = #IdKJ
AND K.druh IN (6,10)
The above is my attempt to rewrite your posted query after tracing the relationships. I did not see any LEFT JOINS that were not superseded by other conditions that forced them into effectively being inner joins, so the above uses inner joins for everything. I have assumed that the druh column is in the TabKontakty table. Otherwise I see no need for that table. I do not guarantee that my re-interpretation is correct though.
How about you create a #temp table for each sub query since the problem is coming up due to the sub queries?
CREATE TABLE #TEMP1
(
LoginID nvarchar(max)
)
CREATE TABLE #TEMP2
(
ko.Prijmeni nvarchar(max),
k.spojeni nvarchar(max),
ko.Cislo nvarchar(max)
)

Which is faster (ISNULL(#SKU, '') = '') or (#SKU IS NULL OR #SKU = '')

There is one if case where I am using IF #SKU IS NULL OR #SKU = '', but my friend says it will take more time as compare to IF ISNULL(#SKU, '') = ''. So you should use IF ISNULL(#SKU, '') = ''. But I think I'm using correct. So please suggest me which one is run faster.
This is my stored procedure:
CREATE PROCEDURE USP_GetExistingRefunds
(
#OrderNo VARCHAR(50),
#SKU VARCHAR(255),
#ProfileID INT
)
AS
BEGIN
--IF ISNULL(#SKU, '') = '' --this work faster or
IF #SKU IS NULL OR #SKU = '' --this work faster
BEGIN
SELECT OrderNo, SKU, ISNULL(Quantity, 0) Quantity, ISNULL(Amount, 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND ProfileID = #ProfileID
END
ELSE
BEGIN
SELECT OrderNo, SKU, ISNULL(SUM(Quantity), 0) Quantity, ISNULL(SUM(Amount), 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND SROI.SKU = #SKU
AND ProfileID = #ProfileID
GROUP BY OrderNo, SKU
END
END
In the context of an IF/ELSE Procedural batch
It doesn't make any difference whatsoever. It literally takes 0.00 MS to determine if a value is blank or unknown, it takes the 0.00MS to determine if ISNULL(#SKU, '') = ''. If there is a difference it would likely be measured in nanoseconds IMO. Again, this in the context of a procedural batch because the statement is only being evaluated once.
In the context of an FILTER (e.g. ON, WHERE or HAVING CLAUSE)
Here the difference is actually enormous, it cannot be understated. This is tricky to explain with parameters and variables involved, so, for brevity, I show you an example with this sample data:
IF OBJECT_ID('tempdb..#things','U') IS NOT NULL DROP TABLE #things;
SELECT TOP (10000) Txt = SUBSTRING(LEFT(NEWID(),36),1,ABS(CHECKSUM(NEWID())%x.N))
INTO #things
FROM (VALUES(1),(30),(40),(NULL)) AS x(N)
CROSS JOIN sys. all_columns;
UPDATE #things SET Txt = NEWID() WHERE txt = '0';
CREATE NONCLUSTERED INDEX nc_things1 ON #things(Txt);
The following queries will find rows that either do or do not contain blanks or nulls
-- Finding things that are blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE t.Txt IS NULL OR t.Txt = '';
-- Finding things that are NOT blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE NOT(t.Txt IS NULL OR t.Txt = '');
SELECT t.Txt
FROM #things AS t
WHERE t.Txt > '';
-- Finding things that are blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE ISNULL(t.Txt,'') = '';
-- Finding things that are NOT blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE ISNULL(t.Txt,'') <> '';
The first three queries are SARGable, the last two are not because of ISNULL. Even though there's an index to help me, the ISNULL renders it useless here. It's the difference between asking someone to look in a phone book for everyone whose name begins with "A" and finding everyone who's name ends with "A".
SARGable predicates allow a query to seek a portion of an index where non-SARGable predicates force a query to scan the entire table REGARDLESS of the how many matching rows exist (if any). When you are dealing with millions/billions of rows joined to many other tables the difference can be a query that runs in seconds to one that, in some cases, may run for hours or even weeks (I've seen a few).
EXECUTION PLANS:
Note that this last one WHERE t.Txt > '' will work too. Any non-null text value is > '' and if t.Txt was NULL then it will also evaluate to false. I include this because this expression works for filtered indexes. The only catch is you can't use it on a text field where Implicit conversion can transform this into the number 0 or less. Note these queries:
IF '' = 0 PRINT 'True' ELSE PRINT 'False'; -- Returns True
IF '' = '0' PRINT 'True' ELSE PRINT 'False'; -- Returns False
IF '' > -1 PRINT 'True' ELSE PRINT 'False'; -- Returns True
IF '' > '-1' PRINT 'True' ELSE PRINT 'False'; -- Returns False
IF #SKU IS NULL OR #SKU ='' is checking null and blank both. In second case if Isnull(#sku,'') you are checking null and assigning '' for null value. Both are different cases.
Which is faster (ISNULL(#SKU, '') = '') or (#SKU IS NULL OR #SKU =
'')
It really doesn't matter in this case. If you were comparing against a column then (SKU IS NULL OR SKU = '') would be preferable as it can use an index but any difference for a single comparison against a variable will be in the order of microseconds and dwarfed by the execution times of the SELECT statements.
To simplify the IF statement I'd probably invert it anyway as below
IF #SKU <> '' --Not null or empty string
BEGIN
SELECT OrderNo, SKU, ISNULL(SUM(Quantity), 0) Quantity, ISNULL(SUM(Amount), 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND SROI.SKU = #SKU
AND ProfileID = #ProfileID
GROUP BY OrderNo, SKU
END
ELSE
BEGIN
SELECT OrderNo, SKU, ISNULL(Quantity, 0) Quantity, ISNULL(Amount, 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND ProfileID = #ProfileID
END
A little long for a comment.
As has been noted by many already, in this case your two options don't really make any appreciable difference. But in the future, when you think of a couple of different ways to code something, there are standard practices you can implement quickly and easily to test for yourself.
At the top of your code block, add this command:
SET STATISTICS TIME, IO ON;
You can use either TIME or IO, but I almost always want to see both, so I always turn them both on at the same time.
The output from this addition will show up in your Messages window after your query or queries run and will give you tangible information about which method is faster, or causes less stress on the SQL Server engine.
You'll want to run a few tests with each option. Warm cache / cold cache especially, but a few iterations is best one way or the other to get an average or eliminate outlier results.
I'm weird, so I always close my code block with:
SET STATISTICS TIME, IO OFF;
But strictly speaking that's unnecessary. I just have a thing about resetting anything I change, just to avoid any possibility of forgetting to reset something that will matter.
Kendra Little has an informative blog post on using STATISTICS.

sql query times out...case / if / what to do

I have the following sql server sproc:
PROCEDURE [dbo].[GetSoftwareProgramsGrid]
#SoftwareTitle varchar(1000)='All',
#CategoryID varchar(100)='All',
#ManufacturerID varchar(50)='All',
#ModelID int=0, -- 0 means all
#AssetID int=0, -- 0 means all
#AssetStatus int=0, --0 is active, 1 is inactive, and 2 is all
#Status int=0, --0 is active, 1 is inactive, and 2 is all
#Type varchar(100)='All',
#Site varchar(100)='All',
#Department varchar(100)='All',
#Manager varchar(100)='All',
#Employee varchar(100)='All',
#SortExpression varchar(100)='Software',
#SortOrder int=0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
*
FROM
(
SELECT DISTINCT
Program AS Software
FROM
AssetProgram ap
LEFT JOIN
AssetAssignment aa
ON
aa.AssetID = ap.AssetID
LEFT JOIN
[MyLinkedServer].MyDB.dbo.Login l
ON
l.LoginID = aa.LoginID
LEFT JOIN
Asset a
ON
a.AssetID = ap.AssetID
INNER JOIN Model m
ON
a.ModelID = m.ModelID
INNER JOIN
Category c
ON
c.CategoryID = m.CategoryID
INNER JOIN Manufacturer ma
ON
ma.ManufacturerID = m.ManufacturerID
WHERE
(
--Software filters
(ap.Program = #SoftwareTitle OR #SoftwareTitle='All')
--Asset filters
AND (c.CategoryID = #CategoryID OR #CategoryID='All') --filter category
AND (ma.ManufacturerID = #ManufacturerID OR #ManufacturerID='All') --filter manufacturer
AND (m.ModelID = #ModelID OR #ModelID = 0) --filter model
AND (a.AssetID = #AssetID OR #AssetID = 0) --filter by asset name (the actual asset id)
AND (((a.Inactive=#AssetStatus) OR (#AssetStatus=2)))
AND (aa.Inactive=0)
AND (ap.Inactive=0)
--Employee filters
/*AND ((l.Inactive=#Status) OR (#Status=2)) --status of employee 2 is all, 1 is inactive, and 0 is active
AND (#Type='All' OR (#Type='Contractor' AND l.IsContractor=1) OR (#Type='Regular' AND l.IsContractor=0)) --contractor or regular employee
AND (#Site='All' OR #Site=l.ClientID) --the site
AND (#Department='All' OR #Department=l.FunctionalGroupID) --the department
AND ((l.Manager = #Manager OR l.FullName=#Manager) OR #Manager='All') --the manager
AND (l.FullName = #Employee OR #Employee='All') --the employee
*/
)) ttt
ORDER BY
CASE WHEN #SortExpression='Software' AND #SortOrder=0 THEN Software END ASC,
CASE WHEN #SortExpression='Software' AND #SortOrder=1 THEN Software END DESC
This query has to include a linked server, due to our setup. The query runs fine and is fast as long as I comment out my employee parameters, namely this section:
--Employee filters
/*AND ((l.Inactive=#Status) OR (#Status=2)) --status of employee 2 is all, 1 is inactive, and 0 is active
AND (#Type='All' OR (#Type='Contractor' AND l.IsContractor=1) OR (#Type='Regular' AND l.IsContractor=0)) --contractor or regular employee
AND (#Site='All' OR #Site=l.ClientID) --the site
AND (#Department='All' OR #Department=l.FunctionalGroupID) --the department
AND ((l.Manager = #Manager OR l.FullName=#Manager) OR #Manager='All') --the manager
AND (l.FullName = #Employee OR #Employee='All') --the employee
*/
The minute I bring even the first line of that section in, for example just this one:
AND ((l.Inactive=#Status) OR (#Status=2))
The entire sproc hangs (times out)....I've properly indexed my tables and I even have an index on the Inactive field within my linked table...If I take that same line above and just say:
AND (l.Inactive=0)
It runs fine, so the OR condition is causing it (boolean). However, I need this condition as a parameter is passed that needs to be satisfied. What are my other options, do I have to IF BEGIN... using all these parameters? It seems cumbersome...For anyones information the AssetProgram table has a total of 50k rows, so that isn't too much.
yeah your where clause is crazy, specially with all the "OR"s
try to load your "users" into a temp table (or var table) based on the #Status variable (if it is 1 you load only inactive, if it is 0 you load only actives and so on...) and use the temp table on the join, that will drastically decrease the number of comparisons.

Switch case in stored procedure to filter out columns

I am puling a report of daily patients in a hospital. My patient_ref_master contains both old and new patients. I have taken 2 columns as new, old. I want to fetch a report in such a way that when the patient is old then the patient id should come under old column else patient id should come under new. I am binding the result of a stored procedure to a report. When I bind the result of my SP to a grid manipulating using datable in C# code it works fine. But when I try using .RDLC report I get all the patient ID's in old and new columns as well. Can I use switch case in my Stored procedure itself to filter it out. Following is my stored procedure.
ALTER PROCEDURE [dbo].[Daily_Report] '2013/08/02'
-- Add the parameters for the stored procedure here
-- Add the parameters for the stored procedure here
(
#date varchar(20)
)
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT
convert(varchar,Patient_Ref_master.creation_Date,105) as 'creation_Date',
Patient_Ref_master.Sr_No as 'sr_No',
old_new_patient.old_new as 'old_new',
Patient_Ref_master.Pat_ID as 'Pat_ID',
Patient_Master.Pat_FName +' '+Patient_Master.Pat_SName as 'NAME',
Dept_ID as 'Dept_ID',
Dept_Master.Dept_Name as 'Dept_Name',
Doc_Master.Doc_ID as 'Doc_Master',
Doc_Master.Doc_FName+' '+Doc_Master.Doc_SName as 'Doc_Name',
Patient_Master.Pat_Addr as 'addr',
Gender_master.Name1 as 'Pat_Sex',
Patient_Master.Age as 'age'
FROM Patient_Ref_master
left join dbo.old_new_patient on dbo.old_new_patient.code=Patient_Ref_master.old_new
left join dbo.Dept_Master on Dept_Master.Dept_code = Patient_Ref_master.Dept_ID
left join Patient_Master on Patient_Master.Pat_Code=Patient_Ref_master.Pat_ID
left join Doc_Master on Doc_Master.Doc_ID=Patient_Ref_master.Doc_ID
left join Gender_master on Gender_master.Code=Patient_Master.Pat_Sex
where
Patient_Ref_master.creation_Date=#date
--MONTH(Patient_Ref_master.creation_Date)=#month and Dept_ID=#dept
order by Patient_Ref_master.Sr_No asc
I want something like the following. where 1st row has the patient for 2nd aug under Patient_ID_New column because he is new and 2nd line because he is old. I want a blank space like 1st row in Patient_Id_Old coumn
Date No Patient_ID_New Patient ID Old
02-08-2013 11 11
02-08-2013 13 1
Yes, you can use case statemnet for the following:
Select
(Case when "condition for oldpatient" Then PatientName Else Null End) Old_Patient,
(Case when "condition for Newpatient" Then PatientName Else Null End) New_Patient
From TableName
Ah, now I get it. I think this is what you want...
SELECT
convert(varchar,Patient_Ref_master.creation_Date,105) as 'creation_Date',
Patient_Ref_master.Sr_No as 'sr_No',
Case When old_new_patient.old_new = 0x1 Then old_new_patient.old_new
Else Patient_Ref_master.Pat_ID
End as 'Pat_ID',
Patient_Master.Pat_FName +' '+Patient_Master.Pat_SName as 'NAME',

How to view ALL COLUMNS (same column names from all table) from all tables starting with Letter A?

Is this possible? I am using ORACLE 10g.
For example: I have 50 tables name A01, A02, A03, A04.........A50.
And all these tables have the "SAME COLUMN NAME"
For example: name, age, location
(Note: The Column Names are the same but not the value in the columns).
In the END... I want to view all data from column: name, age, location FROM ALL tables starting with letter A.
(Note 2: All tables starting with letter A are NOT STATIC, they are dynamic meaning different changes could occur. Example: A01 to A10 could be deleted and A99 Could be added).
Sorry for not clarifying.
DECLARE
TYPE CurTyp IS REF CURSOR;
v_cursor CurTyp;
v_record A01%ROWTYPE;
v_stmt_str VARCHAR2(4000);
BEGIN
for rec in (
select table_name
from user_tables
where table_name like 'A%'
) loop
if v_stmt_str is not null then
v_stmt_str := v_stmt_str || ' union all ';
end if;
v_stmt_str := v_stmt_str || 'SELECT * FROM ' || rec.table_name;
end loop;
OPEN v_cursor FOR v_stmt_str;
LOOP
FETCH v_cursor INTO v_record;
EXIT WHEN v_cursor%NOTFOUND;
-- Read values v_record.name, v_record.age, v_record.location
-- Do something with them
END LOOP;
CLOSE v_cursor;
END;
As per my understanding if you want to view all column names of tables starting with A then try below
select column_name,table_name from user_tab_cols where table_name like 'A%';
If your requirement is something else then specify it clearly.
If understand you correctly and the number of tables is constant then you can create a VIEW once
CREATE VIEW vw_all
AS
SELECT name, age, location FROM A01
UNION ALL
SELECT name, age, location FROM A01
UNION ALL
...
SELECT name, age, location FROM A50
UNION ALL
And then use it
SELECT *
FROM vw_all
WHERE age < 35
ORDER BY name
This returns you all tables you need:
select table_name
from user_tables
where table_name like 'A__';
From this, you can build a dynamic sql statement as:
select listagg('select * from '||table_name,' union all ') within group(order by table_name)
from user_tables
where table_name like 'A__'
This returns actually an SQL statement which contains all tables and the unions:
select * from A01 union all select * from A02 union all select * from A03
And finally execute this via native dynamic sql. You can do that in PL/SQL, so you need a function:
create function getA
query varchar2(32000);
begin
select listagg('select * from '||table_name,' union all ') within group(order by table_name)
into query
from user_tables
where table_name like 'A__';
open res for query;
return res;
end;
Note that what you're doing manually is basically called partitioning, and Oracle has a super-great support already available for that out of the box. I.e. you can have something which looks like a super-huge table, but technically it is stored as a set of smaller tables (and smaller indexes), splitted by a partitioning criteria. For example, if you have millions of payment records, you may partition it by year, this way one physical table contains only a reasonable set of data. Still, you can freely select in this, and if you're hitting data from other partitions, Oracle takes care of pulling those in.

Resources