Teradata - Running query passed as parameter - sql

I am trying to learn Teradata and trying to simplify the way we copy data from production DB to testing DB for testing.
For this process we need to fill up an excel with below details and send to our TDBA:-
Prod Table DbName
Prod Table Name
Prod Table Perm_Size
Prod Table GB Size
Prod Table Record Count
Filter SQL Query to fetch data From Prod DB
Filtered Record Count
I was trying to create a simple SQL utility that accepts parameters (Italics and Bold in the list above) and output the remaining fields.
I kind of started but got stuck in creating and running queries passed as parameters. I also tried using parameters as '?dbName' etc to accept values at run time. But wasn't able to fix that too. Any guidance would be great.
WITH ParamInp(dbName, tblName, fltrQry) AS
(SELECT 'PRDDB', 'EMPL', 'SELECT * FROM PRDVIEWS.EMPL WHERE ID IN (1,2,3)') -- We have select access only on PRDVIEWS schema
SELECT
Upper(Trim(ParamInp.dbName)) AS DATABASENAME,
Substr(Upper(Trim(ParamInp.dbName)), 1, Length(Trim(ParamInp.dbName))-2) || 'VIEWS' AS VIEWNAME, -- Creating View DB schema name
Upper(Trim(ParamInp.tblName)) AS TABLENAME,
fltrQry AS FILTER_QUERY, -- do not want to execute fltrQry here. It is only to include in the excel
Sum(currentperm) AS PERM_SIZE,
Sum(currentperm)/1024**3 AS TOTAL_SIZE, -- GigaByte
(SELECT Cast(Count(*) AS BIGINT) FROM (Substr(Upper(Trim(dbName)), 1, Length(Trim(dbName))-2) || 'VIEWS').Upper(Trim(tblName)) )
AS TOTAL_COUNT, -- Unable to get this working
(SELECT Cast(Count(*) AS BIGINT) FROM (ParamInp.fltrQry))
AS FILTERED_COUNT -- This is where fltrQry should run
FROM dbc.allspace, ParamInp
WHERE TABLENAME = ParamInp.tblName
AND databasename = ParamInp.dbName
GROUP BY 1,2,3
ORDER BY 1,2;
I think I will not be able to do it in one query. In that case, how should I approach this. I run my queries in Teradata SQL assistant and sometimes get lists of tables to be loaded from production.
I expect the output as
DATABASENAME | VIEWNAME | TABLENAME | FILTER_QUERY | PERM_SIZE | TOTAL_SIZE | TOTAL_COUNT | FILTERED_COUNT |
--------------------------------------------------------------------------------------------------------------------------
PRDDB | PRDVIEWS | EMPL | SELECT * FROM PRDVIEWS.EMPL WHERE ID IN (1,2,3) | 1111111 | 2.2 | 333333 | 444 |

Other then Stored procedure you may do it with a two step approach in bteq.
with a select concatenate the needed sql-command(s), inserting the parameters where needed.
the result is exported into a file
run these created sql-commands from the file
I did not test the following, it is intended to show the general idea.
I am sure some additional tweaking is necessary to get the syntax of generated command(s) right.
.logon tdpid/user,pass
.set format off
.set titledashes off
.export file /tmp/myQuery.bteq
select 'SELECT
Upper(Trim('||dbName||')) AS DATABASENAME,
Substr(Upper(Trim('||dbName||')), 1, Length(Trim('||dbName||'))-2) || 'VIEWS AS VIEWNAME,
Upper(Trim('||tblName||')) AS TABLENAME,
'||fltrQry||' AS FILTER_QUERY,
Sum(currentperm) AS PERM_SIZE,
Sum(currentperm)/1024**3 AS TOTAL_SIZE,
(SELECT Cast(Count(*) AS BIGINT) FROM (Substr(Upper(Trim('||dbName||')), 1, Length(Trim('||dbName||'))-2) || 'VIEWS).Upper(Trim('||tblName||')) )
AS TOTAL_COUNT,
(SELECT Cast(Count(*) AS BIGINT) FROM ('||fltrQry||'))
AS FILTERED_COUNT
FROM dbc.allspace
WHERE TABLENAME = '||tblName||'
AND databasename = '||dbName||'
GROUP BY 1,2,3
ORDER BY 1,2;' (TITLE '')
from (
SELECT 'PRDDB' as dbName, 'EMPL' as tblName, 'SELECT * FROM PRDVIEWS.EMPL WHERE ID IN (1,2,3)' as fltrQry
) as commands;
.export reset
.run file = /tmp/myQuery.bteq

Related

How do I select a SQL dataset where values in the first row are the column names?

I have data that looks like this:
ID RowType Col_1 Col_2 Col_3 ... Col_n
1 HDR FirstName LastName Birthdate
2 DTL Steve Bramblet 1989-01-01
3 DTL Bob Marley 1967-03-12
4 DTL Mickey Mouse 1921-04-25
And I want to return a table or dataset that looks like this:
ID FirstName LastName Birthdate
2 Steve Bramblet 1989-01-01
3 Bob Marley 1967-03-12
4 Mickey Mouse 1921-04-25
where n = 255 (so there's a limit of 255 Col_ fields)
***EDIT: The data in the HDR row is arbitrary so I'm just using FirstName, LastName, Birthdate as examples. This is why I thought it will need to be dynamic SQL since the column names I want to end up with will change based on the values in the HDR row. THX! ***
If there's a purely SQL solution that is what I'm after. It's going into an ETL process (SSIS) so I could use a Script task if all else fails.
Even if I could return a single row that would be a solution. I was thinking there might be a dynamic sql solution for something like this:
select Col_1 as FirstName, Col_2 as LastName, Col_3 as Birthdate
Not sure if your first data snippet is already in a oracle table or not but it is in a CSV file then you have option during loading to skip headers.
If data is already in table then you can use UNION to get desired result
Select * from table name where rowtype=‘HRD’
union
select * from table name where rowtype=‘DTL’
If you need First Name etc as Column header then you need not to do anything. Design destination table columns as per your requirement.
Sorry, posted an answer but I completely misread that you had your desired column headers as data in the source table.
One trivial solution (though it requires more IO) would be to dump the table data to a flat file without headers, then read it back in, but this time tell SSIS that the first row has headers, and ignore the RowType column. Make sure you sort the data correctly before writing it out to the intermediate file!
To dump to a file without headers, you have to set ColumnNamesInFirstDataRow to false. Set this in the properties window, not by editing the connection. More info in this thread
If you have a lot of data, this is obviously very inefficient.
Try the following using row_number. Here is the demo.
with cte as
(
select
*,
row_number() over (order by id) as rn
from myTable
)
select
ID,
Col_1 as FirstName,
Col_2 as LastName,
Col_3 as Birthdate
from cte
where rn > 1
output:
| id | firstname | lastname | birthdate |
| --- | --------- | -------- | ---------- |
| 2 | Steve | Bramblet | 1989-01-01 |
| 3 | Bob | Marley | 1967-03-12 |
| 4 | Mickey | Mouse | 1921-04-25 |
Oh, well. There is a pure SSIS approach, assumed the source is a SQL table. Here it is, rather sketchy.
Create a Variable oColSet with type Object, and 255 variables of type String and names sColName_1, sColName_2 ... sColName_255.
Create a SQL Task with query like select top(1) Col_1, Col_2, ... Col_255 from Src where RowType = 'HDR', set task properties ResultSet = Full Result Set, on result set tab - set Result Name to 0 and Variable Name to oColSet.
Add ForEach Loop enumerator, set it as ForEach ADO Enumerator, ADO object source variable - set to oColSet, Enumeration mode = Rows in the first table. Then, on the Variable Mappings tab - define as such example (Variable - Index) - sColName_1 - 0, sColName_2 - 1, ... sColName_255 - 254.
Create a variable sSQLQuery with type String and Variable Expression like
"SELECT Col_1 AS ["+#[User::sColName_1]+"],
Col_2 AS ["+#[User::sColName_2]+"],
...
Col_255 AS ["+#[User::sColName_255]+"]
FROM Src WHERE RowType='DTL'"
In the ForEach Loop - add your dataflow, in the OLEDB Source - set Data access mode to SQL command from variable and provide variable name User::sSQLQuery. On the Data Flow itself - set DelayValidation=true.
The main idea of this design - retrieve all column names and store it in temp variable (step 2). Then step 3 does parsing and places all results into corresponding variables, 1 column (0th) - into sColName_1 etc. Step 4 defines a SQL command as an expression, which is evaluated every time when the variable is read. Finally, in the ForEach Loop (where parsing is done) - you perform your dataflow.
Limitations of SSIS - data types and column names should be the same at runtime as at design time. If you need to further store your dataset into SQL - let me know, so I could adjust the proposed solution.

Data field - search and write value in new data field (Oracle)

Sorry, I don't know how to describe that as a title.
With a query (example: Select SELECT PKEY, TRUNC (CREATEDFORMAT), STATISTICS FROM BUSINESS_DATA WHERE STATISTICS LIKE '% business_%'), I can display all data that contains the value "business_xxxxxx".
For example, the data field can have the following content: c01_ad; concierge_beendet; business_start; or also skill_my; pre_initial_markt; business_request; topIntMaster; concierge_start; c01_start;
Is it now possible in a temp-only output the corresponding value in another column?
So the output looks like this, for example?
PKEY | TRUNC(CREATEDFORMAT) | NEW_STATISTICS
1 | 13.06.2020 | business_start
2 | 14.06.2020 | business_request
That means removing everything that does not start with business_xxx? Is this possible in an SQL query? RegEx would not be the right one, I think.
I think you want:
select
pkey,
trunc(createdformat) createddate,
regexp_substr(statistics, 'business_\S*') new_statistics
from business_data
where statistics like '% business_%'
You can also use the following regexp_substr:
SQL> select regexp_substr(str,'business_[^;]+') as result
2 from
3 --sample data
4 (select 'skill_my; pre_initial_markt; business_request; topIntMaster; concierge_start; c01_start;' as str from dual
5 union all
6 select 'c01_ad; concierge_beendet; business_start;' from dual);
RESULT
--------------------------------------------------------------------------------
business_request
business_start
SQL>

How to perform this SQL query?

I have these 2 tables, proc and users. I am creating a web site where users can post proclomations and people can make a search and find them according the search criteria. I want to display users' profile picture next to their proclomations. Rest of the data are in the same table so I only have problem with pp.
I get the syntax error when I try to run this query.
Users table:
| ID | username| usersurname| image | email | countryCode | mobile | pwd |
Proc table
proc_id | p_name | p_surname |uid | p_from | p_where | etc. (rest is not relevant)
I've tried this on phpmyadmin:
SELECT * FROM proc WHERE p_from = 'Baku' UNION
SELECT users.image FROM users INNER JOIN ON proc WHERE proc.uid = users.id
But it doesn't work.
This is how I was displaying results before. Now I also want to select pp from image column from users table
$sql = "SELECT * FROM proc WHERE p_from = '".$from."' and p_where = '".$where."' and type= '".$type."' and p_date >= '".$procDate."'
ORDER BY p_date, price LIMIT " . $page_first_result . " , " . $results_per_page . "";
SELECT
users.image, proc.*
FROM
users
JOIN
proc
ON
proc.uid=users.id
WHERE ...
ORDER BY ...
LIMIT ...
Please, NEVER write this: p_from = '".$from."'. I do mean, NEVER. Read up on this: https://www.w3schools.com/sql/sql_injection.asp . Then NEVER write that again.

What are the technical differences between "select * from table_name" and "select a.* from table_name a"?

This might be a basic question, but I can't find explanations after googling around.
Anyway, a short background story. I have this table that I don't have the permission to alter on DB2:
other_field | date_field | time_field
---------------------------------------
1 | 180101 | 101010
2 | 180102 | 202020
3 | 180103 | 303030
4 | 180104 | 404040
I tried to use:
select *, concat(date_field, time_field) as TIME
from Table_Name
My expected result is displaying something like this:
other_field | date_field | time_field | TIME
--------------------------------------------------------
1 | 180101 | 101010 | 180101101010
2 | 180102 | 102020 | 180102102020
3 | 180103 | 103030 | 180103103030
4 | 180104 | 104040 | 180104104040
But I can't use that query for some reason. It gave me an error ...Token , was not valid. Valid tokens: FROM INTO that basically said a comma (,) after * is invalid.
Then I tried tweaking it a little into:
select a.*, concat(a.date_field, a.time_field) as TIME
from Table_Name a
And it works!
I understand that Table_Name a are often used for joining tables, but I'm curious about the underlying mechanism.
What are the technical differences between using Table_Name and Table_Name a? And what is this a called?
Technically there will be no difference between the op of
SELECT * FROM TAB_NAME and SELECT a,* FROM TAB_NAME a.
Here you are just specifying alias name.
But you can understand the difference when you will try to fetch another column with * from TAB_NAME.
That means if you want to gate data as bellow
SELECT *,COL_1,COL2...
FROM TAB_NAME
or
SELECT *,CONCAT(...)
FROM TAB_NAME
or anything with * you must have to specify the alias name.
But the question is why? Let me try to explain,
As you know here SELECT * means you are trying to select all columns. So, * means "all" and if you are putting * after SELECT clause that means you already have given a command to your system to select all by passing a special character and after that your system can only expect FROM clause instead of any other thing. Because you already told your system/database to select all then there would be nothing left to select and hence your system will always wait for FROM clause. So it will throw an error each and every time.
BUT now the question is, how the bellow query will work internally
SELECT a.*,COL_1,COL2...
FROM TAB_NAME a
or
SELECT a.*,a.COL_1,a.COL2...
FROM TAB_NAME a
or
SELECT a.*,CONCAT(c1,c2)
FROM TAB_NAME a
or
SELECT a.*,CONCAT(a.c1,a.c2)
FROM TAB_NAME a
or anything else like that.
Here your system will understand that you are trying to select all from table a that means you may select any other col/function etc from either table a or any other table. That's the reason why your system/database will allow you to insert other col/func also after a, if required or you can use from clause as well after a.*
Db2 (LUW) 11.1 support this syntax
create table Table_Name (
other_field int not null primary key
, date_field date not null
, time_field time not null
)
;
insert into Table_Name values
(1,'2018-01-01', '10.10.10')
, (2,'2018-01-01', '20.20.20')
, (3,'2018-01-01', '13.13.13')
, (4,'2018-01-01', '14.14.14')
;
select *, timestamp(date_field, time_field) as TIME from Table_Name
;
which will return
OTHER_FIELD DATE_FIELD TIME_FIELD TIME
----------- ---------- ---------- ---------------------
1 2018-01-01 10:10:10 2018-01-01 10:10:10.0
2 2018-01-01 20:20:20 2018-01-01 20:20:20.0
3 2018-01-01 13:13:13 2018-01-01 13:13:13.0
4 2018-01-01 14:14:14 2018-01-01 14:14:14.0
BTW I took the liberty of using sensible data types for your example. Use DATE1, TIME and TIMESAMP (or TIMESTAMP(0)) for working with date and time values...

How do you concat multiple rows into one column in SQL Server?

I've searched high and low for the answer to this, but I can't figure it out. I'm relatively new to SQL Server and don't quite have the syntax down yet. I have this datastructure (simplified):
Table "Users" | Table "Tags":
UserID UserName | TagID UserID PhotoID
1 Bob | 1 1 1
2 Bill | 2 2 1
3 Jane | 3 3 1
4 Sam | 4 2 2
-----------------------------------------------------
Table "Photos": | Table "Albums":
PhotoID UserID AlbumID | AlbumID UserID
1 1 1 | 1 1
2 1 1 | 2 3
3 1 1 | 3 2
4 3 2 |
5 3 2 |
I'm looking for a way to get the all the photo info (easy) plus all the tags for that photo concatenated like CONCAT(username, ', ') AS Tags of course with the last comma removed. I'm having a bear of a time trying to do this. I've tried the method in this article but I get an error when I try to run the query saying that I can't use DECLARE statements... do you guys have any idea how this can be done? I'm using VS08 and whatever DB is installed in it (I normally use MySQL so I don't know what flavor of DB this really is... it's an .mdf file?)
Ok, I feel like I need to jump in to comment about How do you concat multiple rows into one column in SQL Server? and provide a more preferred answer.
I'm really sorry, but using scalar-valued functions like this will kill performance. Just open SQL Profiler and have a look at what's going on when you use a scalar-function that calls a table.
Also, the "update a variable" technique for concatenation is not encouraged, as that functionality might not continue in future versions.
The preferred way of doing string concatenation to use FOR XML PATH instead.
select
stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
,*
from photos
order by photoid;
For examples of how FOR XML PATH works, consider the following, imagining that you have a table with two fields called 'id' and 'name'
SELECT id, name
FROM table
order by name
FOR XML PATH('item'),root('itemlist')
;
Gives:
<itemlist><item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item></itemlist>
But if you leave out the ROOT, you get something slightly different:
SELECT id, name
FROM table
order by name
FOR XML PATH('item')
;
<item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item>
And if you put an empty PATH string, you get even closer to ordinary string concatenation:
SELECT id, name
FROM table
order by name
FOR XML PATH('')
;
<id>2</id><name>Aardvark</a><id>1</id><name>Zebra</name>
Now comes the really tricky bit... If you name a column starting with an # sign, it becomes an attribute, and if a column doesn't have a name (or you call it [*]), then it leaves out that tag too:
SELECT ',' + name
FROM table
order by name
FOR XML PATH('')
;
,Aardvark,Zebra
Now finally, to strip the leading comma, the STUFF command comes in. STUFF(s,x,n,s2) pulls out n characters of s, starting at position x. In their place, it puts s2. So:
SELECT STUFF('abcde',2,3,'123456');
gives:
a123456e
So now have a look at my query above for your taglist.
select
stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
,*
from photos
order by photoid;
For each photo, I have a subquery which grabs the tags and concatenates them (in order) with a commma and a space. Then I surround that subquery in a stuff command to strip the leading comma and space.
I apologise for any typos - I haven't actually created the tables on my own machine to test this.
Rob
I'd create a UDF:
create function GetTags(PhotoID int) returns #tags varchar(max)
as
begin
declare #mytags varchar(max)
set #mytags = ''
select #mytags = #mytags + ', ' + tag from tags where photoid = #photoid
return substring(#mytags, 3, 8000)
end
Then, all you have to do is:
select GetTags(photoID) as tagList from photos
Street_Name ; Street_Code
west | 14
east | 7
west+east | 714
If want to show two different row concat itself , how can do it?
(I mean last row i want to show from select result. My table had first and secord record)