zend_db obtain "select ..., (select ...) as x from ... " - zend-framework

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(...);

Related

Can somebody help me translate this into postgresql?

I am very new to SQL and I do not know much about writing code in the different DBMS. I am trying to write a report in our school's MOODLE platform, which uses postgresql, using a configurable report found here. However, the code does not work in postgresql. In particular, how do I rewrite those lines with variable assignments like #prevtime := to make the code work in postgresql?
Here is the complete code from the link.
SELECT
l.id,
l.timecreated,
DATE_FORMAT(FROM_UNIXTIME(l.timecreated),'%d-%m-%Y') AS dTime,
#prevtime := (SELECT MAX(timecreated) FROM mdl_logstore_standard_log
WHERE userid = %%USERID%% AND id < l.id ORDER BY id ASC LIMIT 1) AS prev_time,
IF (l.timecreated - #prevtime < 7200, #delta := #delta + (l.timecreated-#prevtime),0) AS sumtime,
l.timecreated-#prevtime AS delta,
"User" AS TYPE
FROM prefix_logstore_standard_log AS l,
(SELECT #delta := 0) AS s_init
# CHANGE UserID
WHERE l.userid = %%USERID%% AND l.courseid = %%COURSEID%%
%%FILTER_STARTTIME:l.timecreated:>%% %%FILTER_ENDTIME:l.timecreated:<%%
This is supposed to report the time spent by students in courses in MOODLE.
I assume the original query was written for MySQL. You haven't explained what the query actually does, but the #prevtime hack is usually a workaround for missing window functions, so most probably this can be done using lag() in Postgres, something along the lines:
select l.id,
l.timecreated,
to_char(to_timestamp(l.timecreated), 'dd-mm-yyyy') as dtime,
lag(timecreated) over w as prev_time,
l.timecreated - lag(timecreated) over w as delta,
'User' as type,
FROM prefix_logstore_standard_log AS l
window w as (partition by userid order by id)
WHERE l.userid = %%USERID%%
AND l.courseid = %%COURSEID%%

Insert from several temporary tables

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();

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

Zend_Db_Select: LEFT JOIN on a subselect

I have a query, that does a LEFT JOIN on a subselect. This query is run in a high load environment and performs within the set requirements. The query (highly simplified) looks like:
SELECT
table_A.pKey
, table_A.uKey
, table_A.aaa
, table_B.bbb
, alias_C.ccc
, alias_C.ddd
FROM table_A
INNER JOIN table_B ON table_A.pKey = table_B.pKey
LEFT JOIN (
SELECT
table_X.pKey
, table_X.ccc
, table_Y.ddd
FROM table_X
INNER JOIN table_Y ON table_X.pKey = table_Y.pKey
) AS alias_C ON table_A.uKey = alias_C.pKey;
(for various reasons, it is not possible to rewrite the subselect as a (direct) LEFT JOIN).
Now, I cannot get the LEFT JOIN on subselect to work with Zend_Db_Select. I've tried everything I could come up with, but it does not work.
So my question is:
Is it not possible to do a query as described above with Zend_Db_Select?
What syntax do I need to get it to work within Zend Framework?
I think that it should work like this:
$subselect = $db->select->from(array('x' => 'table_X'), array('x.pKey', 'x.ccc', 'y.ddd'), 'dbname')
->join(array('Y' => 'table_Y'), 'x.pkey = y.pkey', array(), 'dbname');
$select = $db->select->from(array('a' => 'table_A'), array(/*needed columns*/), 'dbname')
->join(array('b' => 'table_B'), 'a.pkey = b.pkey', array(), 'dbname')
->joinLeft(array('c' => new Zend_Db_Expr('('.$subselect.')'), 'c.pkey = a.ukey', array())
I haven't tried it but I believe it'll work.
...
->joinLeft(array('c' => new Zend_Db_Expr('(' . $subselect->assemble() . ')'), 'c.pkey = a.ukey', array())

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';