I have a variable called c_kilometers. I have a cursor that grabs a bunch of records that have those kilometers. I need to run two separate SELECT statements in the cursor that simply grab a kilometer from one table based on values in the cursor, and run another SELECT doing the same thing on another table.
SELECT t.kilometers INTO c_kilometers
FROM table_name WHERE WHERE l.code = cursor_t.code_att
SELECT g.kilometers INTO c_kilometers
FROM table_name WHERE l.code = cursor_t.code_aff
My question is can I add the c_kilometers together without creating a temporary variable to hold on of the values? I haven't used PL/SQL in awhile, and I don't remember having to do this ever, so this is more of a learning question than anything.
Provided that both your queries always return exactly one row, you can do either of the following:
/* Variant 1 */
SELECT t.kilometers + g.kilometers
INTO c_kilometers
FROM table_name t, table_name2 g
WHERE etc1
AND etc2
/* Variant 2 */
SELECT t.kilometers
INTO c_kilometers
FROM table_name
WHERE etc;
SELECT c_kilometers + g.kilometers
INTO c_kilometers
FROM table_name2
WHERE etc;
If they're in the same table as you posted, you can use:
SELECT COALESCE(SUM(kilometers), 0)
INTO c_kilometers
FROM table_name
WHERE l.code IN (cursor_t.code_aff, cursor_t.code_att)
It seems that it will be more efficient to put table_name into your SELECT query that produces the cursor.
If you post this query, I'll probably can help to do this.
Join the SELECTs like this:
SELECT a.kilometers + b.kilometers
FROM (SELECT code, kilometers FROM table_name WHERE ...) a JOIN
(SELECT code, kilometers FROM table_name WHERE ...) b ON a.code = b.code
Related
I want all tables from my schema that match a list of other tables with similar names from a different schema. I am using the following query:
SELECT TABLE_NAME, COUNT(COLUMN_NAME) ColCount
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'schema_2' and TABLE_NAME LIKE ANY (array[
'%table1%',
'%table2%',
'%table3%',
'%table4%',
'%table5%'])
I want another column added to the output which specifies which name in the array the table from schema_2 matched, i.e.
TABLE_NAME COL_COUNT SCHEMA_1_TABLE
table1a 15 table1
Is there a way to do this?
You could try using a join. Kind of like:
WITH a AS (SELECT unnest(array['%table1%','%table2%','%table3%','%table4%','%table5%'])
AS table)
SELECT
isc.TABLE_NAME,
COUNT(isc.COLUMN_NAME),
REPLACE(a.table,'%','')
FROM
INFORMATION_SCHEMA.COLUMNS isc
LEFT JOIN a ON (isc.TABLE_NAME LIKE a.table)
WHERE
isc.TABLE_SCHEMA = 'schema_2'
AND a.table IS NOT NULL
GROUP BY
isc.TABLE_NAME,
a.table
You could also join INFORMATION_SCHEMA.COLUMNS with INFORMATION_SCHEMA.TABLES in order to just detect any duplication, not just the ones specified.
How to combine these 2 sets of code?
Tables: geregistreerd g, instrument i and indeling id.
I want to display g.voornaammuzikant, g.achternaammuzikant, i.naaminstrument, id.familie.
Table g is linked with table i with instrumentid
Table i is linked with table id with indelingid
--oefening 1:
SELECT TRIM(' ' FROM g.voornaammuzikant), TRIM(' ' FROM g.achternaammuzikant),
(SELECT i.naaminstrument
from instrument i
where i.instrumentid = g.instrumentid) as "naam instrument"
from geregistreerd g
order by g.voornaammuzikant;
--oefening 2:
SELECT i.naaminstrument,
(SELECT id.familie
from indeling id
where i.indelingid = id.indelingid) as "familie",
(SELECT id.onderfamilie
from indeling id
where i.indelingid = id.indelingid) as "onderfamilie"
from instrument i
order by i.naaminstrument;
COMBINED
SELECT TRIM(' ' FROM g.voornaammuzikant),
TRIM(' ' FROM g.achternaammuzikant),
(SELECT i.naaminstrument
(SELECT id.familie
from indeling id
where i.indelingid = id.indelingid) as "familie"
from instrument i
where i.instrumentid = g.instrumentid) as "naam instrument"
from geregistreerd g
order by g.voornaammuzikant;
Why would you want to do things in a complicated way, if they can be simplified? What's wrong with a simple join? Something like this:
select g.voornaammuzikant,
g.achternaammuzikant,
i.naaminstrument,
id.familie
from geregistreerd g join instrument i on i.instrumentid = g.instrumentid
join indeling id on id.indelingid = i.indelingid
order by g.voornaammuzikant;
By the way, those TRIMs you posted don't look good. SELECT TRIM(' ' FROM g.voornaammuzikant) is certainly wrong; I don't know what you meant to say with that. Perhaps removing superfluous spaces from those values? If they exist, is - by any chance - that column's datatype CHAR? If so, consider switching to VARCHAR2 because the former pads values with spaces up to the whole length of that column.
Suppose I have an sql query like the following (I realize this query could be written better, just bear with me):
SELECT aT.NAME
FROM anothertable aT,
( SELECT ts.slot_id,
tgm.trans_id,
tagm.agent_id
FROM slots ts,
transactions tgm,
agents tagm
WHERE ts.slot_id = (12345, 678910)
and ts.slot_id = tagm.slot_id
AND ts.slot_id = tgm.slot_id) INNER
WHERE INNER.trans_id = aT.trans_id
AND INNER.agent_id = aT.trans_id
Now suppose that I need to break up this query into two parts...in the first I'll execute the inner query, do some processing on the results in code, and then pass back a reduced set to the outer part of the query. The question is, is there an easy way to emulate an inner table in sql?
For instance, if the results of the inner query returned 5 rows but my program deems to only need two of those rows, how can I write sql that will do what I am trying to do below? Is there a way, in sql, to declare a table for in memory in query use?
SELECT
at.Name
FROM
anotherTable aT,
(SLOT_ID, TRANS_ID, AGENT_ID
-------------------------
230743, 3270893, 2307203
078490, 230897, 237021) inner
WHERE
inner.trans_id = at.trans_id
AND INNER.agent_id = aT.trans_id
Just use a subquery:
SELECT at.Name
FROM anotherTable aT JOIN
(select 230743 as SLOT_ID, 3270893 as TRANS_ID, 2307203 as AGENT_ID from dual
select 078490, 230897, 237021 from dual
) i
on i.trans_id = at.trans_id AND i.agent_id = aT.trans_id;
Most systems will let you define a TEMP TABLE or TABLE VARIABLE: https://www.simple-talk.com/sql/t-sql-programming/temporary-tables-in-sql-server/
CREATE TABLE #temp (
SLOT_ID INT,
TRANS_ID INT,
AGENT_ID INT
);
INSERT INTO #temp(SLOT_ID, TRANS_ID, AGENT_ID)
(--inner query goes here)
--do your main query, then:
DROP TABLE #temp
IN MS SQL Server (not sure about other systems), you could possibly use a Common Table Expression (CTE): https://technet.microsoft.com/en-us/library/ms190766%28v=sql.105%29.aspx
WITH inner AS (
--inner query goes here
)
--main select goes here
Personally, since I generally work with MSSQL Server, I use CTE's quite a bit, as they can be created "on the fly", and can be a big help in organizing more complex queries.
The subquery method worked. Since this is Oracle, the syntax turned out to be:
SELECT aT.Name
FROM anotherTable aT,
(select 1907945 as SLOT_ID, 2732985 as TRANS_ID, 40157 as AGENT_ID FROM DUAL
union
select 1907945, 2732985, 40187 FROM DUAL
) inner
WHERE
inner.trans_id = aT.trans_id AND INNER.agent_id = aT.trans_id;
selecting column which is another table column value.what's wrong with this query
select
geoName
from
hgeo h
where
geoName not in (select (select column_name
from information_schema.columns
where ((column_name = h.levelName) and (table_Name = 'flatgeo')))
from flatgeo)
I think there is some problem
(select
(select column_name
from information_schema.columns
where ((column_name = h.levelName) and (table_Name = 'flatgeo')))
How to write it?
You can't use meta-information to change the shape of your query. Unless you want to go down the dynamic sql route (which can quickly get complex), I'd go for the following:
SELECT
geoName
FROM hgeo h
WHERE
NOT EXISTS (SELECT * FROM flatgeo f
WHERE
(h.levelName = 'value1' and f.value1 = h.geoName) OR
(h.levelName = 'value2' and f.value2 = h.geoName)
//Repeat for each possible levelName value.
)
you can try this
select GeoName from HGeo where HGeo.GeoName not in
(select FlatGeo.value1 from FlatGeo union
select FlatGeo.value2 from FlatGeo union
select FlatGeo.value3 from FlatGeo)
You should use tablename before your column geoname. Like hgeo.geoname.
do you want to find out the geoname of hgeo that are not in flatgeo?
I have a query like the following
select *
from (
select *
from callTableFunction(#paramPrev)
.....< a whole load of other joins, wheres , etc >........
) prevValues
full join
(
select *
from callTableFunction(#paramCurr)
.....< a whole load of other joins, wheres , etc >........
) currValues on prevValues.Field1 = currValues.Field1
....<other joins with the same subselect as the above two with different parameters passed in
where ........
group by ....
The following subselect is common to all the subselects in the query bar the #param to the table function.
select *
from callTableFunction(#param)
.....< a whole load of other joins, wheres , etc >........
One option is for me to convert this into a function and call the function, but i dont like this as I may be changing the
subselect query quite often for.....or I am wondering if there is an alternative using CTE
like
with sometable(#param1) as
(
select *
from callTableFunction(#param)
.....< a whole load of other joins, wheres , etc >........
)
select
sometable(#paramPrev) prevValues
full join sometable(#currPrev) currValues on prevValues.Field1 = currValues.Field1
where ........
group by ....
Is there any syntax like this or technique I can use like this.
This is in SQL Server 2008 R2
Thanks.
What you're trying to do is not supported syntax - CTE's cannot be parameterised in this way.
See books online - http://msdn.microsoft.com/en-us/library/ms175972.aspx.
(values in brackets after a CTE name are an optional list of output column names)
If there are only two parameter values (paramPrev and currPrev), you might be able to make the code a little easier to read by splitting them into two CTEs - something like this:
with prevCTE as (
select *
from callTableFunction(#paramPrev)
.....< a whole load of other joins, wheres , etc
........ )
,curCTE as (
select *
from callTableFunction(#currPrev)
.....< a whole load of other joins, wheres , etc
........ ),
select
prevCTE prevValues
full join curCTE currValues on
prevValues.Field1 = currValues.Field1 where
........ group by
....
You should be able to wrap the subqueries up as parameterized inline table-valued functions, and then use them with an OUTER JOIN:
CREATE FUNCTION wrapped_subquery(#param int) -- assuming it's an int type, change if necessary...
RETURNS TABLE
RETURN
SELECT * FROM callTableFunction(#param)
.....< a whole load of other joins, wheres , etc ........
GO
SELECT *
FROM
wrapped_subquery(#paramPrev) prevValues
FULL OUTER JOIN wrapped_subquery(#currPrev) currValues ON prevValues.Field1 = currValues.Field1
WHERE ........
GROUP BY ....
After failing to assign scalar variables before with, i finally got a working solution using a stored procedure and a temp table:
create proc hours_absent(#wid nvarchar(30), #start date, #end date)
as
with T1 as(
select c from t
),
T2 as(
select c from T1
)
select c from T2
order by 1, 2
OPTION(MAXRECURSION 365)
Calling the stored procedure:
if object_id('tempdb..#t') is not null drop table #t
create table #t([month] date, hours float)
insert into #t exec hours_absent '9001', '2014-01-01', '2015-01-01'
select * from #t
There may be some differences between my example and what you want depending on how your subsequent ON statements are formulated. Since you didn't specify, I assumed that all the subsequent joins were against the first table.
In my example I used literals rather than #prev,#current but you can easily substitute variables in place of literals to achieve what you want.
-- Standin function for your table function to create working example.
CREATE FUNCTION TestMe(
#parm int)
RETURNS TABLE
AS
RETURN
(SELECT #parm AS N, 'a' AS V UNION ALL
SELECT #parm + 1, 'b' UNION ALL
SELECT #parm + 2, 'c' UNION ALL
SELECT #parm + 2, 'd' UNION ALL
SELECT #parm + 3, 'e');
go
-- This calls TestMe first with 2 then 4 then 6... (what you don't want)
-- Compare these results with those below
SELECT t1.N AS AN, t1.V as AV,
t2.N AS BN, t2.V as BV,
t3.N AS CN, t3.V as CV
FROM TestMe(2)AS t1
FULL JOIN TestMe(4)AS t2 ON t1.N = t2.N
FULL JOIN TestMe(6)AS t3 ON t1.N = t3.N;
-- Put your #vars in place of 2,4,6 adding select statements as needed
WITH params
AS (SELECT 2 AS p UNION ALL
SELECT 4 AS p UNION ALL
SELECT 6 AS p)
-- This CTE encapsulates the call to TestMe (and any other joins)
,AllData
AS (SELECT *
FROM params AS p
OUTER APPLY TestMe(p.p)) -- See! only coded once
-- Add any other necessary joins here
-- Select needs to deal with all the columns with identical names
SELECT d1.N AS AN, d1.V as AV,
d2.N AS BN, d2.V as BV,
d3.N AS CN, d3.V as CV
-- d1 gets limited to values where p = 2 in the where clause below
FROM AllData AS d1
-- Outer joins require the ANDs to restrict row multiplication
FULL JOIN AllData AS d2 ON d1.N = d2.N
AND d1.p = 2 AND d2.p = 4
FULL JOIN AllData AS d3 ON d1.N = d3.N
AND d1.p = 2 AND d2.p = 4 AND d3.p = 6
-- Since AllData actually contains all the rows we must limit the results
WHERE(d1.p = 2 OR d1.p IS NULL)
AND (d2.p = 4 OR d2.p IS NULL)
AND (d3.p = 6 OR d3.p IS NULL);
What you want to do is akin to a pivot and so the complexity of the needed query is similar to creating a pivot result without using the pivot statement.
Were you to use Pivot, duplicate rows (such as I included in this example) would be aggreagted. This is also a solution for doing a pivot where aggregation is unwanted.