Insert from several temporary tables - postgresql

I want to rewrite the following query using jooq:
with first_temp as (
select a.id as lie_id
from first_table a
where a.some_Field = 100160
), second_temp as (
select b.id as ben_id
from second_table b
where b.email = 'some.email#gmail.com'
) insert into third_table (first_table_id, second_table_id)
select a.lie_id, b.ben_id from first_temp a, second_temp b;
I was trying something like the following:
DriverManager.getConnection(url, login, password).use {
val create = DSL.using(it, SQLDialect.POSTGRES)
create.with("first_temp").`as`(create.select(FIRST_TABLE.ID.`as`("lie_id")))
.with("second_temp").`as`(create.select(SECOND_TABLE.ID.`as`("ben_id")))
.insertInto(THIRD_TABLE, THIRD_TABLE.FIRST_TABLE_ID, THIRD_TABLE.SECOND_TABLE_ID)
.select(create.select().from("first_temp", "second_temp"), create.select().from("second_temp")))
}
But without success.

Your fixed query
// You forgot FROM and WHERE clauses in your CTEs!
create.with("first_temp").`as`(
create.select(FIRST_TABLE.ID.`as`("lie_id"))
.from(FIRST_TABLE)
.where(FIRST_TABLE.SOME_FIELD.eq(100160)))
.with("second_temp").`as`(
create.select(SECOND_TABLE.ID.`as`("ben_id"))
.from(SECOND_TABLE)
.where(SECOND_TABLE.EMAIL.eq("some.email#gmail.com")))
.insertInto(THIRD_TABLE, THIRD_TABLE.FIRST_TABLE_ID, THIRD_TABLE.SECOND_TABLE_ID)
// You had too many queries in this part of the statement, and
// didn't project the two columns you were interested int
.select(create.select(
field(name("first_temp", "lie_id")),
field(name("second_temp", "ben_id")))
.from("first_temp", "second_temp"))
// Don't forget this ;-)
.execute();
But frankly, why even use CTE at all? Your query would be much simpler like this, both in SQL and in jOOQ (assuming that you really want this cartesian product):
Better SQL Version
insert into third_table (first_table_id, second_table_id)
select a.id, b.id
from first_table a, second_table b
where a.some_field = 100160
and b.email = 'some.email#gmail.com';
Better jOOQ Version
create.insertInto(THIRD_TABLE, THIRD_TABLE.FIRST_TABLE_ID, THIRD_TABLE.SECOND_TABLE_ID)
.select(create.select(FIRST_TABLE.ID, SECOND_TABLE.ID)
.from(FIRST_TABLE, SECOND_TABLE)
.where(FIRST_TABLE.SOME_FIELD.eq(100160))
.and(SECOND_TABLE.EMAIL.eq("some_email#gmail.com")))
.execute();

Related

Need help in creating CriteriaQuery

First of all, I would like to know if it is possible to do?
Below is my query and I am trying to build using criteria.
SELECT CONCAT('record-', rl.record_id) AS tempId,
'sloka' AS type,
rl.record_id AS recordId,
rl.title AS title,
rl.locale as locale,
rl.intro AS intro,
rl.title AS localetitle,
NULL AS audioUrl,
lp.name AS byName,
lp.person_id AS byId,
lp.name AS onName,
lp.person_id AS onId
FROM record_locale rl
LEFT JOIN record r ON rl.record_id = r.record_id
LEFT JOIN locale_person lp ON r.written_on = lp.person_id
WHERE rl.title LIKE :title
AND rl.locale = :locale
AND lp.locale = :locale
UNION
SELECT CONCAT('lyric-', s.song_id) AS tempId,
'bhajan' AS type,
s.song_id AS recordId,
s.title,
l.locale as locale,
NULL AS intro,
l.title AS localetitle,
s.audio_url AS audioUrl,
lpb.name AS byName,
lpb.person_id AS byId,
lpo.name AS onName,
lpo.person_id AS onId
FROM song s
LEFT JOIN locale_person lpb
ON (s.written_by = lpb.person_id AND lpb.locale = :locale)
LEFT JOIN locale_person lpo
ON (s.written_on = lpo.person_id AND lpo.locale = lpb.locale)
INNER JOIN lyric l
ON (l.locale = lpb.locale AND l.song_id = s.song_id)
WHERE s.title LIKE :title AND s.approved_by IS NOT NULL
ORDER BY localeTitle ASC
// END
Based on few conditions, I might need to have union of both queries or just individual query without union.
Converting the SQL to JPQL is usually a good first step, as we can't quite tell what these tables map to, or what you are expecting to get back. If it is possible to do in JPQL, it should be possible with a criteria query. Except in this case: JPA/JPQL does not have the union operator so it won't work in straight JPA, but some providers such as EclipseLink have support. See:
UNION to JPA Query
and
http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/j_union.htm

Joining with set-returning function (SRF) and access columns in SQLAlchemy

Suppose I have an activity table and a subscription table. Each activity has an array of generic references to some other object, and each subscription has a single generic reference to some other object in the same set.
CREATE TABLE activity (
id serial primary key,
ob_refs UUID[] not null
);
CREATE TABLE subscription (
id UUID primary key,
ob_ref UUID,
subscribed boolean not null
);
I want to join with the set-returning function unnest so I can find the "deepest" matching subscription, something like this:
SELECT id
FROM (
SELECT DISTINCT ON (activity.id)
activity.id,
x.ob_ref, x.ob_depth,
subscription.subscribed IS NULL OR subscription.subscribed = TRUE
AS subscribed,
FROM activity
LEFT JOIN subscription
ON activity.ob_refs #> array[subscription.ob_ref]
LEFT JOIN unnest(activity.ob_refs)
WITH ORDINALITY AS x(ob_ref, ob_depth)
ON subscription.ob_ref = x.ob_ref
ORDER BY x.ob_depth DESC
) sub
WHERE subscribed = TRUE;
But I can't figure out how to do that second join and get access to the columns. I've tried creating a FromClause like this:
act_ref_t = (sa.select(
[sa.column('unnest', UUID).label('ob_ref'),
sa.column('ordinality', sa.Integer).label('ob_depth')],
from_obj=sa.func.unnest(Activity.ob_refs))
.suffix_with('WITH ORDINALITY')
.alias('act_ref_t'))
...
query = (query
.outerjoin(
act_ref_t,
Subscription.ob_ref == act_ref_t.c.ob_ref))
.order_by(activity.id, act_ref_t.ob_depth)
But that results in this SQL with another subquery:
LEFT OUTER JOIN (
SELECT unnest AS ob_ref, ordinality AS ref_i
FROM unnest(activity.ob_refs) WITH ORDINALITY
) AS act_ref_t
ON subscription.ob_refs #> ARRAY[act_ref_t.ob_ref]
... which fails because of the missing and unsupported LATERAL keyword:
There is an entry for table "activity", but it cannot be referenced from this part of the query.
So, how can I create a JOIN clause for this SRF without using a subquery? Or is there something else I'm missing?
Edit 1 Using sa.text with TextClause.columns instead of sa.select gets me a lot closer:
act_ref_t = (sa.sql.text(
"unnest(activity.ob_refs) WITH ORDINALITY")
.columns(sa.column('unnest', UUID),
sa.column('ordinality', sa.Integer))
.alias('act_ref'))
But the resulting SQL fails because it wraps the clause in parentheses:
LEFT OUTER JOIN (unnest(activity.ob_refs) WITH ORDINALITY)
AS act_ref ON subscription.ob_ref = act_ref.unnest
The error is syntax error at or near ")". Can I get TextAsFrom to not be wrapped in parentheses?
It turns out this is not directly supported by SA, but the correct behaviour can be achieved with a ColumnClause and a FunctionElement. First import this recipe as described by zzzeek in this SA issue. Then create a special unnest function that includes the WITH ORDINALITY modifier:
class unnest_func(ColumnFunction):
name = 'unnest'
column_names = ['unnest', 'ordinality']
#compiles(unnest_func)
def _compile_unnest_func(element, compiler, **kw):
return compiler.visit_function(element, **kw) + " WITH ORDINALITY"
You can then use it in joins, ordering, etc. like this:
act_ref = unnest_func(Activity.ob_refs)
query = (query
.add_columns(act_ref.c.unnest, act_ref.c.ordinality)
.outerjoin(act_ref, sa.true())
.outerjoin(Subscription, Subscription.ob_ref == act_ref.c.unnest)
.order_by(act_ref.c.ordinality.desc()))

zend_db obtain "select ..., (select ...) as x from ... "

I have a query that i can't build with Zend_Db_Select
SELECT `f`.*,
(SELECT Sum(x) AS `y`
FROM z AS pf
WHERE pf.q_id = f.id) AS w
FROM f ...
WHERE ...
GROUP BY `f`.`id`
so at the moment i'm running it manually $db->fetchAll($sql).
How do i obtain
select f.* , (select ...) as `something` from ...
I was thinking using ->column('f.*, (select...)') but it didn't work,
it could work maybe with a left join if i do (select ..., id) and then join on that id, but i wanted to obtain THIS very sql query. Is it possible?
thanks
I would recommend the JOIN. You might get better performance as sub selects are usually hard for the database to optimize. It is also easy to write this with Zend_Db_Select. Alternately new Zend_Db_Expr might work for this.
$select = $db->select()
->from('f', array('f.foo', 'f.bar', new Zend_Db_Expr('SELECT Sum(x) AS `y`
FROM z AS pf
WHERE pf.q_id = f.id') => 'f'))
->where(...);

TSQL CTE Error: Incorrect syntax near ')'

I am developing a TSQL stored proc using SSMS 2008 and am receiving the above error while generating a CTE. I want to add logic to this SP to return every day, not just the days with data. How do I do this? Here is my SP so far:
ALTER Proc [dbo].[rpt_rd_CensusWithChart]
#program uniqueidentifier = NULL,
#office uniqueidentifier = NULL
AS
DECLARE #a_date datetime
SET #a_date = case when MONTH(GETDATE()) >= 7 THEN '7/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30))
ELSE '7/1/' + CAST(YEAR(GETDATE())-1 AS VARCHAR(30)) END
if exists (
select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#ENROLLEES')
) DROP TABLE #ENROLLEES;
if exists (
select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#DISCHARGES')
) DROP TABLE #DISCHARGES;
declare #sum_enrollment int
set #sum_enrollment =
(select sum(1)
from enrollment_view A
join enrollment_info_expanded_view C on A.enrollment_id = C.enroll_el_id
where
(#office is NULL OR A.group_profile_id = #office)
AND (#program is NULL OR A.program_info_id = #program)
and (C.pe_end_date IS NULL OR C.pe_end_date > #a_date)
AND C.pe_start_date IS NOT NULL and C.pe_start_date < #a_date)
select
A.program_info_id as [Program code],
A.[program_name],
A.profile_name as Facility,
A.group_profile_id as Facility_code,
A.people_id,
1 as enrollment_id,
C.pe_start_date,
C.pe_end_date,
LEFT(datename(month,(C.pe_start_date)),3) as a_month,
day(C.pe_start_date) as a_day,
#sum_enrollment as sum_enrollment
into #ENROLLEES
from enrollment_view A
join enrollment_info_expanded_view C on A.enrollment_id = C.enroll_el_id
where
(#office is NULL OR A.group_profile_id = #office)
AND (#program is NULL OR A.program_info_id = #program)
and (C.pe_end_date IS NULL OR C.pe_end_date > #a_date)
AND C.pe_start_date IS NOT NULL and C.pe_start_date >= #a_date
;WITH #ENROLLEES AS (
SELECT '7/1/11' AS dt
UNION ALL
SELECT DATEADD(d, 1, pe_start_date) as dt
FROM #ENROLLEES s
WHERE DATEADD(d, 1, pe_start_date) <= '12/1/11')
The most obvious issue (and probably the one that causes the error message too) is the absence of the actual statement to which the last CTE is supposed to pertain. I presume it should be a SELECT statement, one that would combine the result set of the CTE with the data from the #ENROLLEES table.
And that's where another issue emerges.
You see, apart from the fact that a name that starts with a single # is hardly advisable for anything that is not a local temporary table (a CTE is not a table indeed), you've also chosen for your CTE a particular name that already belongs to an existing table (more precisely, to the already mentioned #ENROLLEES temporary table), and the one you are going to pull data from too. You should definitely not use an existing table's name for a CTE, or you will not be able to join it with the CTE due to the name conflict.
It also appears that, based on its code, the last CTE represents an unfinished implementation of the logic you say you want to add to the SP. I can suggest some idea, but before I go on I'd like you to realise that there are actually two different requests in your post. One is about finding the cause of the error message, the other is about code for a new logic. Generally you are probably better off separating such requests into distinct questions, and so you might be in this case as well.
Anyway, here's my suggestion:
build a complete list of dates you want to be accounted for in the result set (that's what the CTE will be used for);
left-join that list with the #ENROLLEES table to pick data for the existing dates and some defaults or NULLs for the non-existing ones.
It might be implemented like this:
… /* all your code up until the last WITH */
;
WITH cte AS (
SELECT CAST('7/1/11' AS date) AS dt
UNION ALL
SELECT DATEADD(d, 1, dt) as dt
FROM cte
WHERE dt < '12/1/11'
)
SELECT
cte.dt,
tmp.[Program code],
tmp.[program_name],
… /* other columns as necessary; you might also consider
enveloping some or all of the "tmp" columns in ISNULLs,
like in
ISNULL(tmp.[Program code], '(none)') AS [Program code]
to provide default values for absent data */
FROM cte
LEFT JOIN #ENROLLEES tmp ON cte.dt = tmp.pe_start_date
;

tsql : Access query to TSQL union update conversion correct?

I have a query in access which i need to convert to a stored proc in sql server 2005.
the query in access is as follows:
UPDATE
tblitem,
tblFileSignature
SET
tblitem.strFileProcesstype = [tblFileSignature].[STRFILEPROCESSTYPE], tblitem.strFileSignatureType = [tblFileSignature].[strfilesignaturetype]
WHERE
(((tblitem.strFileSignatureType) Is Null) AND
((tblitem.strFileExclude)="n") AND
((InStr([tblitem].[strfilesignature],[tblFileSignature].[strsignature]))=1) AND ((tblitem.uidItemType)=1 Or (tblitem.uidItemType)=5) AND
((tblitem.uidCollection)=[forms]![frmSetup]![txtInputCol]) AND ((tblitem.strFileSignature) Not Like "d0c*") AND
((tblFileSignature.strFileProcessType) Not Like "ZIP"));
in tsql.. would this be the same?
update tblItem
set
i.strFileProcesstype = f.strFileProcesstype,
i.strFileSignatureType = f.strfilesignaturetype
from tblItem as I UNION tblFileSignature as F
WHERE (((i.strFileSignatureType) Is Null) AND
((i.strFileExclude)="n") AND
((i.[strfilesignature] like F.strsignature)) AND
((i.uidItemType)=1 Or
(i.uidItemType)=5) AND
((i.uidCollection)=#inputcolumn AND
((i.strFileSignature) Not Like 'd0c%') AND
((F.strFileProcessType) Not Like 'ZIP'));
thanks in advance
UPDATE:
so i'm going with the following. if i uncomment the declare and select clause and just execute from the declare down, it runs, if i comment the declare and select parts, it says error near ';'.
UPDATE I
SET
I.strFileProcesstype = F.STRFILEPROCESSTYPE,
I.strFileSignatureType = F.strfilesignaturetype
--declare #uidcollectionID int
--select I.strFileSignatureType
from
tblItem I
inner join tblFileSignature F
on
I.strfilesignature = left(F.strsignature,len(I.strfilesignature))
WHERE I.strFileSignatureType Is Null
AND I.strFileExclude='n'
AND I.uidItemType in (1,5)
AND I.uidCollection = #uidCollectionID
AND left(I.strFileSignature,3) <> 'd0c'
AND F.strFileProcessType <> 'ZIP';
any ideas?
You should change the
Double Quotes to Single Quotes
* to %
Replace the InStr with LIKE
Other than that, it looks fine to me.
No, you'd use a JOIN, not a UNION.
You can either make it a CROSS JOIN, and continue to apply the join conditions in the WHERE clause, or you can make it an inner join:
from tblItem as I INNER JOIN tblFileSignature as F
ON ((InStr(i.[strfilesignature],F.[strsignature]))=1)
And remove that condition from the WHERE clause (Lieven's answer also applies).
This should be close to what you need. May need to work on the join condition, but I think my conversion from INSTR will do it.
UPDATE i
SET strFileProcesstype = fs.STRFILEPROCESSTYPE,
strFileSignatureType = fs.strfilesignaturetype
FROM tblitem i
INNER JOIN tblFileSignature fs
ON i.strfilesignature = LEFT(fs.strsignature, LEN(i.strfilesignature))
WHERE i.strFileSignatureType IS Null
AND i.strFileExclude='n'
AND i.uidItemType IN (1,5)
AND i.uidCollection = #inputcolumn
AND LEFT(i.strFileSignature,3) <> 'd0c'
AND fs.strFileProcessType <> 'ZIP';