LOOP/FOR statement on oracle script - sql

I'm running an ansible playbook that runs an sqlplus script to an Oracle DB.
Basically the script creates a CSV file with some server info. The query is pretty much autogenerated, so it will be difficult change it.
set markup csv on
spool 'playbook-dir/files/servers.csv'
SELECT *
FROM (SELECT DISTINCT server.primary_name SERVER_NAME,
server.arpa_domain ARPA_DOMAIN,
server.impact IMPACT,
instance_definition.category SOLUTION_CATEGORY,
instance_definition.instance_name SOLUTION_NAME,
instance_on_server.ins_instance_name INSTANCE_NAME
FROM server_db.instance_definition, server_db.instance_on_server, server_db.business, server_db.server_customer, server_db.server
WHERE ( server_db.instance_definition.app_id(+) = server_db.instance_on_server.app_id )
AND ( server_db.server.system_id = server_db.instance_on_server.system_id(+) )
AND ( ( instance_definition.instance_name LIKE '%windows%' )
OR ( instance_definition.instance_name LIKE '%centos%' ) )
AND ( instance_on_server.status LIKE 'in production' )
AND ( server_db.business.business_id(+) = server_db.server_customer.business_id )
AND ( server_db.server_customer.system_id(+) = server_db.server.system_id )
AND (( instance_definition.category LIKE 'os' ))
AND (( server.primary_name||'.'||server.arpa_domain LIKE '%' ))
AND business.secure_access_r <> 1)
WHERE ROWNUM <= 600000 + 1
ORDER BY server_name;
spool off
The problem is that this query brings all the 5000 server and I need only 200.
I want to add a LOOP/FOR statement with only the servers I need, but I think I'm doing something wrong.
This is the query with the LOOP:
declare
type table_varchar is table of varchar2(10);
var_table_varchar table_varchar;
begin
var_table_varchar := table_varchar('server1', 'server2', 'server3', 'server4');
for elem in 1 .. var_table_varchar.count loop
SELECT *
FROM (SELECT DISTINCT server.primary_name SERVER_NAME,
server.arpa_domain ARPA_DOMAIN,
server.impact IMPACT,
instance_definition.category SOLUTION_CATEGORY,
instance_definition.instance_name SOLUTION_NAME,
instance_on_server.ins_instance_name INSTANCE_NAME
FROM server_db.instance_definition, server_db.instance_on_server, server_db.business, server_db.server_customer, server_db.server
WHERE ( server_db.instance_definition.app_id(+) = server_db.instance_on_server.app_id )
AND ( server_db.server.system_id = server_db.instance_on_server.system_id(+) )
AND ( ( instance_definition.instance_name LIKE '%windows%' )
OR ( instance_definition.instance_name LIKE '%centos%' ) )
AND ( instance_on_server.status LIKE 'in production' )
AND ( server_db.business.business_id(+) = server_db.server_customer.business_id )
AND ( server_db.server_customer.system_id(+) = server_db.server.system_id )
AND (( instance_definition.category LIKE 'os' ))
AND (( server.primary_name||'.'||server.arpa_domain LIKE '%' ))
AND (( server.primary_name LIKE '%var_assoc_varchar(elem)%' ))
AND business.secure_access_r <> 1)
WHERE ROWNUM <= 600000 + 1
ORDER BY server_name;
end loop;
end;
When I run it I get this error:
Error report -
ORA-06550: line 9, column 5:
PLS-00428: an INTO clause is expected in this SELECT statement
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Sadly, I'm not much of a DBA and my knowledge got me so far. So suggestion will be appreciated

You can not use plain select sql without into clause like this in PL/SQL.
What are you expecting out of the query in PL/SQL?
Your first query executed and filled csv because it is single query and not anonymous block.
You can simply add condition into where clause of your first query as follows:
server.primary_name in ('server1', 'server2', 'server3', 'server4')

Related

Converting SQL Server Query to Oracle

I have the SQL Server query shown below and I am trying to get it to work on Oracle. I have done some looking but am new to Oracle so I am not even sure what I should be looking for. I am hoping that I can make the a query to run adhoc not necessarily a procedure. The basic concept is the following:
Create a temporary table to hold data to be totaled.
Create a date table to get a list of dates.
Build a dynamic query to insert data into the temporary table.
Execute the dynamic query.
Select the summary from the temporary table.
Drop the temporary table
Please remember, I am new to Oracle and I have done some looking. I know that the variables and aliases must be formatted differently and can get the date table but I am not sure the proper Oracle way to create and execute dynamic queries where the table name is the dynamic part. I can create the dynamic string with the correct table name but don't know how to execute it. I have seen some examples but none of them seem to make sense to me for what I am trying to do.
-- Oracle query for dates
with dt (d) as (
select last_day(add_months(sysdate,-2))+1 + rownum - 1
from all_objects
where rownum <= sysdate-last_day(add_months(sysdate,-2))+1+1
)
select 'insert into #tt (cnt, eem, ers, sts) (
select count(1), eem_id, ers_id, sts_id
from event_error' || to_char(D, 'ddmmyy') || ' eve
group by sts_id, eem_id, ers_id); ' "QRY"
from dt;
What I have done in the past is create a bash script which would do the looping through each date and then used the script to summarize the output. This time however I am trying to learn something and I know that there has to be a way to do this in SQL in Oracle.
I appreciate any help or assistance and hope I have explained this well enough.
-- Working SQL Server query
-- declare variables
declare #query varchar(max);
-- create temporary table
create table #tt(cnt int, eem int, ers int, sts int);
-- get a list of dates to process
with dt (d) as
(
select dateadd(month, datediff(month, 0, getdate())-1, 0) as d
union all
select dateadd(dd, 1, d)
from dt
where dateadd(dd, 1, d) <= getdate()
)
-- build the dynamic query
select distinct
#query = stuff ((select 'insert into #tt (cnt, eem, ers, sts) (
select count(1), eem_id, ers_id, sts_id
from event_error' + replace(convert(varchar(5), d, 103), '/', '') + right(year(d), 2) + ' (nolock) eve
group by sts_id, eem_id, ers_id); '
from dt for xml path, type).value(N'.[1]',N'nvarchar(max)')
, 1, 1, N'')
from dt;
-- to execute the dynamic query
execute (#query);
-- query the temporary table
select
[Stream] = sts.sts_name,
[Count] = sum(eve.cnt),
[Error Status] = ers.ers_name,
[Error Number] = eem.eem_error_no,
[Error Text] = eem.eem_error_text
from
#tt eve
inner join
event_error_message eem on eem.eem_id = eve.eem
inner join
error_status ers on ers.ers_id = eve.ers
inner join
stream_stage sts on sts.sts_id = eve.sts
group by
sts.sts_name, eem.eem_error_no, eem.eem_error_text, ers.ers_name
order by
sts.sts_name, eem.eem_error_no, ers.ers_name;
-- drop the temporary table
drop table #tt;
So as I expected, after fighting this all day and finally giving up and asking for help I have an answer. The query below works however if you have improvements or constructive criticism please share as I said, I am trying to learn.
-- create the temporary table
create global temporary table my_tt (
cnt number
, sts number
, eem number
, ers number
)
on commit delete rows;
declare
V_TABL_NM ALL_TABLES.TABLE_NAME%TYPE;
V_SQL VARCHAR2(1024);
begin
for GET_TABL_LIST in (
with dt (d) as (
select last_day(add_months(sysdate,-2))+1 + rownum -1
from all_objects
where rownum <= sysdate-last_day(add_months(sysdate,-2))
)
select 'event_error' || to_char(D, 'ddmmyy') TABLE_NAME from dt
) loop
V_TABL_NM := GET_TABL_LIST.TABLE_NAME;
V_SQL := 'insert into my_tt select count(1), sts_id, eem_id, ers_id from ' || V_TABL_NM || ' group by sts_id, eem_id, ers_id';
execute immediate V_SQL;
end loop;
end;
/
-- the slash is important for the above statement to complete
select
sts.sts_name "Stream"
, sum(eve.cnt) "Count"
, ers.ers_name "Error Status"
, eem.eem_error_no "Error Number"
, eem.eem_error_text "Error Text"
from my_tt eve
inner join event_error_message eem
on eem.eem_id = eve.eem
inner join error_status ers
on ers.ers_id = eve.ers
inner join stream_stage sts
on sts.sts_id = eve.sts
group by sts.sts_name, eem.eem_error_no, eem.eem_error_text, ers.ers_name
order by sts.sts_name, eem.eem_error_no, ers.ers_name;
-- drop the temporary table
drop table my_tt purge;

SQL Server - Only one expression can be specified in the select list when the subquery is not introduced with EXISTS. ErrorCode HY000

I'm trying to execute the query bellow on a SQL server database
but it throws the following error:
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS.
Code:
IF #status = 1
BEGIN
..... -- execute something
END
ELSE
BEGIN
select top #URL.nof# *
from lead_feed
where
lead_tstamp > '2017-09-01'
and egoiID is not null
and rtrim(ltrim(lead_name)) <> ''
and rtrim(ltrim(lead_surname)) <> ''
and rtrim(ltrim(lead_gender)) <> ''
and rtrim(ltrim(lead_interest)) <> ''
and lead_country in (select campaign_country
from client_campaigns
where campaignID = '#URL.ccampaignID#')
and leadID not in (select leadID
from lead_integration
where campaignID = '#URL.ccampaignID#')
order by
lead_tstamp desc
END
It points to the leadID not in ( .... part, if I delete this part it points the same error to and lead_country in ( ... part.
Can you please explain me whats happening?
Thanks.

SQL CASE in WHERE Incorrect Syntax

Good Afternoon
Im programming in SQL and i have this problem
The Code is:
select
item.TX_Commercial_Family as 'Familia Comercial',
sum(nota.NM_Invoice_Quantity) as 'Quantidade Faturada' ,
sum(nota.NM_Material) as 'Valor Faturado'
from DW_DTS_Item item
INNER JOIN DIS_DTS_Invoice_Fact nota ON item.SK_Item = nota.SK_Item
inner join DW_DTS_Representative usuario on nota.SK_Representative=usuario.SK_Representative
inner join DW_DTS_Operation_Nature cfop on cfop.SK_Operation_Nature=nota.SK_Operation_Nature
where
item.CD_Inventory_Group='30'
and (
cfop.CD_CFOP='5101'
OR cfop.CD_CFOP='6101'
OR cfop.CD_CFOP='6107'
OR cfop.CD_CFOP='6108'
OR cfop.CD_CFOP='6109'
OR cfop.CD_CFOP='6113'
OR cfop.CD_CFOP='6401'
)
and(
case
when :number = '01' then nota.SK_Representative='05'
end
)
and nota.SK_Currency='1'
and nota.CD_Country='BRASIL'
and nota.DT_Sale_Forma_Bill_Exit between '2016-09-01' and '2016-09-30'
--and extract (year from nota.DT_Sale_Forma_Bill_Exit) = extract(year from CURRENT_DATE)
group by item.TX_Commercial_Family,usuario.TX_Representative,usuario.SK_Representative
order by usuario.TX_Representative asc
But when I compile the code, the following error appears
SQL Error [102][S0001]:Incorrect syntax near '='.
The point is that I have to pass a parameter and through this parameter filter through which SK_Representative will appear in the code.
I've tried everything but the error continues to appear
According to the error you are probably using SQL Server.
Replace :number with #number.
e.g.
declare #number int = 1;
select #number + 1;
set #number = #number * 10;
select #number;
As you want to use number as parameter then use '#' instead of ':'
So use the following snippet where u are using case statements.
and(
case
when #number = '01' then nota.SK_Representative='05'
end
)

Transact SQL Subquery calling a function incorrect syntax

I get an incorrect syntax near '.' and can't seem to identify why in the following code:
select
o.object_id,
(select top 1 Zone from dbo.getzone(o.object_id)) as Zone from object as o
getzone is a table-valued Function that works perfectly when I reference it directly, or if I put a specific object_id in, but everytime I try to make it dynamic, I get the syntax error.
What am I missing?
You can't do that. You need to have a scalar version that returns only one result. It can be just a wrapper script if you want. Something like this:
CREATE FUNCTION [dbo].[getSingleZone](#object_id varchar(20))
RETURNS varchar(20)
AS
BEGIN
DECLARE #Zone varchar(20)
select #Zone = max(Zone) from dbo.getzone(#object_id)
return #Zone
END
select
o.object_id,
dbo.getSingleZone(o.object_id) as Zone from object o
I don't know your data types, so I guessed.
Fix your alias
select o.object_id,
(select top 1 Zone from dbo.getzone(o.object_id)) as Zone
from object AS o
Perhaps I'm missing the problem, but this seems to work. Using the name of a built-in function (OBJECT_ID) as a column name might not be helping.
SQL fiddle example or code below.
-- TVF without parameter.
create function dbo.GetZone()
returns table as
return
select Id, Letter
from
( values ( 1, 'Aleph' ), ( 2, 'Beth' ), ( 3, 'Gimmel' ) ) as Letters( Id, Letter );
go
-- TVF with parameter;
create function dbo.GetZone2( #Id as Int )
returns table as
return
select Id, Letter
from dbo.GetZone() where Id = #Id;
go
select * from dbo.GetZone();
select * from dbo.GetZone2( 2 );
-- Sample table and data.
declare #Objects as table ( Id Int Identity, Letter VarChar(16) );
insert into #Objects values ( 'Alpha' ), ( 'Beta' ), ( 'Gamma' );
select * from #Objects;
-- Correlated subquery.
select O.Id, O.Letter as [Greek],
( select top 1 Letter from dbo.GetZone( ) where Id = O.Id ) as [Hebrew]
from #Objects as O;
select O.Id, O.Letter as [Greek],
( select top 1 Letter from dbo.GetZone2( O.Id ) ) as [Hebrew]
from #Objects as O;
-- Houseclean.
drop function dbo.GetZone;
drop function dbo.GetZone2;

How to execute dynamic SQL in Teradata

Is there any way to submit dynamically generated SQL to Teradata? I've written a query that will create the code to denormalize a table. Right now, I am pulling the code down to my client (SAS) and resubmitting it in a second step. I am not familiar with either Teradata macros or procedures; would something like that work?
To illustrate, I have a table defined like this:
create multiset table MYTABLE
( RECID integer generated always as identity
( start with 1
increment by 1
minvalue -2147483647
maxvalue 2147483647
no cycle )
, SNAP_DATE date format 'YYYY/MM/DD'
, EMAIL_DATE date format 'YYYY/MM/DD'
, FREQ integer
)
unique primary index ( RECID )
The table is populated every day (SNAP_DATE) and is used to monitor changes to an email_date in another table. The following query returns the code that I can run to create my denormalized view:
select RUN_THIS
from (
select RUN_THIS, RN
from (
select 'select EMAIL_DATE ' (varchar(100)) as RUN_THIS
, 0 (int) as RN
) x
union all
select ', sum( case when SNAP_DATE = date '''
|| (SNAP_DATE (format 'yyyy-mm-dd') (char(10)) )
|| ''' then FREQ else 0 end ) as D'
|| (SNAP_DATE (format 'yyyymmdd') (char(8)) ) as RUN_THIS
, row_number() over ( partition by 1 order by SNAP_DATE ) as RN
from ( select distinct SNAP_DATE
from MYTABLE
where SNAP_DATE > current_date - 30) t1
union all
select RUN_THIS, RN
from (
select 'from MYTABLE group by 1 order by 1;' as RUN_THIS
, 10000 as RN
) y
) t
order by RN
I export the result of the above query to a file on my client, then turn around and submit that file back to Teradata. I'm hoping there is some way to store this complete definition in some Teradata object so it can be executed directly.
You may find success putting this in a stored procedure using the DBC.SysExecSQL command.
Here is an overly simplified example of a stored procedure in Teradata. Obviously in production would want an error handler defined to address things like invalid database objects. Furthermore, you could return the SQLSTATE back as a parameter to test for whether the stored procedure completed successfully or not.
CREATE PROCEDURE SYSDBA.CommentDatabase
(
IN P_Database VARCHAR(30),
IN P_Comment VARCHAR(255),
OUT MSG
)
MAIN: --Label
BEGIN
DECLARE P_SQL_TEXT VARCHAR(4000);
SET P_SQL_TEXT='COMMENT ON DATABASE '||P_DATABASE||' AS '''||P_COMMENT||'''';
CALL dbc.SysExecSQL (:P_SQL_TEXT);
SET MSG = 'Database '||P_DBNAME||' commented successfully!';
END;