I am attempting to get all the records from a table where trans_type='RM' but if there is no trans_type='RM' i want to return all the records where trans_type = 'AD'
Technically im using xtupls MetaSQL on a PostgreSQL server so a solution using either is great I can upload my metaSQL statement need be but I really just need a way to do
-- Group: lotserial
-- Name: detail
-- Notes:
-- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
-- See www.xtuple.com/EULA for the full text of the software license.
SELECT ls_number,
ls_notes,
formatlotserialnumberbarcode(ls_number) AS lotserial_barcode,
item_number,
item_descrip1,
item_descrip2,
charass_char_id,
charass_value,
poitem_id,
poitem_vend_item_descrip,
char_name,
formatqty(itemloc_qty) as lotqty,
lshist.*
FROM
itemloc,
ls
JOIN item ON (item_id=ls_item_id)
LEFT JOIN charass ON (charass_target_id=ls_id)
LEFT JOIN "char" ON (char_id=charass_char_id),
lshist (<? value("itemid") ?>,<? value("warehouseid") ?>,ls_number,
<? value("pattern") ?>,<? value("transType") ?>,<? value("startDate") ?>,
<? value("endDate") ?>,<? value("trace") ?>,1)
LEFT JOIN pohead ON(pohead_number=(TRIM(SUBSTRING(lshist_ordernumber FROM '-.*-'),'-')))
LEFT JOIN poitem ON(poitem_pohead_id=pohead_id)
<? if exists('ls_id') ?>
WHERE ls_id=<? value("ls_id") ?>
<? endif ?>
<? if exists('ls_number') ?>
WHERE ls_number=<? value("ls_number") ?>
<? endif ?>
AND lshist_warehous_code='PS'
<? if exists(TRIM(SUBSTRING(lshist_ordernumber FROM '.*-'),'-')='PO')?>
AND poitem_linenumber = CAST(TRIM(SUBSTRING(lshist_ordernumber FROM '[^-]*$'),'-') AS INTEGER)
<? endif ?>
AND ls_id = itemloc_ls_id
AND charass_target_type = 'LS'
/*
<? if exists(lshist_transtype='RM')?>
AND lshist_transtype='RM'
<? elseif exists(lshist_transtype='AD')?>
AND lshist_transtype='AD'
<? elseif exists(lshist_transtype='RL')?>
AND lshist_transtype='RL'
<? elseif exists(lshist_transtype='SH')?>
AND lshist_transtype='SH'
<? elseif exists(lshist_transtype='IM')?>
AND lshist_transtype='IM'
<? elseif exists(lshist_transtype='TR')?>
AND lshist_transtype='TR'
<? elseif exists(lshist_transtype='RP')?>
AND lshist_transtype='RP'
<? endif ?>
You could use a common table expression:
WITH rm AS (
SELECT * FROM my_table WHERE trans_type = 'RM'
)
SELECT *
FROM data
UNION ALL
SELECT * FROM my_table
WHERE trans_type = 'AD'
AND NOT EXISTS (
SELECT * FROM rm
)
This will avoid the second scan as can be seen in an EXPLAIN ANALYZE call, but there's still a little overhead compared to making the decision in the client, probably due to the CTE materialization (which is PostgreSQL-specific).
I've benchmarked this for an small data set. There seems to be a 5% - 10% overhead in PostgreSQL over running two queries from pgplsql. So, in most cases, and for simple queries like this one, Laurenz's solution is preferrable.
There may be more complex query setups, where the single query is preferrable to two separate queries, as the single query can re-use intermediate results.
I would just run two queries, the first with WHERE trans_type='RM', and only run a second query with WHERE trans_type='AD' if the first one returns an empty result.
I think that trying to squish that into a single query would make things overly complicated and probably also would not be faster – I cannot think of a way that avoids a second scan.
Related
I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.
I have written below SQL in an RPGLE program. Intent is to update the header file (TC400F) if no corresponding records exist in detail file (TC401F). Are there better ways of doing this? By better, I mean that would make the query run faster or would make it look more cleaner.
Exec SQL UPDATE TC400F
SET T40STS = '05',
T40OFL = '1'
WHERE T40SID = :K#T41SID AND
T40PID = :K#T41PID AND
NOT EXISTS (SELECT * FROM TC401F WHERE
T41SID = :K#T41SID AND
T41PID = :K#T41PID );
try this :
Exec SQL UPDATE TC400F f1
SET (f1.T40STS, f1.T40OFL) = ('05', '1')
WHERE f1.T40SID = :K#T41SID AND
f1.T40PID = :K#T41PID AND
NOT EXISTS
(
SELECT * FROM TC401F f2
WHERE (f1.T41SID, f1.T41PID) = (f2.T41SID, f2.T41PID)
);
You have not done anything here that explicitly restricts performance. The optimizer is pretty smart these days. Most of the things that affect SQL performance are external to the statement. Best to write the statement to be semantically correct (as you have above), and let the optimizer do it's thing. Then if you see performance issues, then investigate them with then Explain tools in Run SQL Scripts. Most likely your performance issues will derive from incorrect indexes.
Exec SQL UPDATE TC400F
SET T40STS = '05',
T40OFL = '1'
WHERE (t40sts <> '05' or t40ofl <> '1')
and T40SID = :K#T41SID
AND T40PID = :K#T41PID
and
NOT EXISTS (SELECT onefieldhere FROM TC401F WHERE
T41SID = :K#T41SID AND
T41PID = :K#T41PID );
Add a little optimistic code don't update what is already set.
Please don't make the as400 an orphan by ending a line with an operator that is the start of the next line. Optimistic updates are probably fastest when values are already set because it knows its already done. You could make it better by using the alias field names so someone in the future will know what a t40ofl is. Select only one field from the exists clause so sql won't have to pull in a whole row.
I have tried every single expalnation on this but I keep getting errors please help me out guys this might have been answered but its over a week I keep struggleing with it.
I have two Tables
Student and Certificate
the two tables have a relationship on student, i have a unique column student reg no and on certificate student reg which are bot the same values.
I am trying to select data from the two table into a table in html but its not working
here is my statement
<?php
$sql = "SELECT * s.studentsregno
s.fullname
s.program
c.certificateno AS cert_no
c.dateofissue AS date_ofissue
c.status AS pick_status
FROM student s
JOIN certificate c on c.studentreg = s.studentregno";
$result = $db->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()){
echo
'<tr class="odd gradeX">
<td>'.$row["studentregno"].'</td>
<td>'.$row["fullname"].'</td>
<td>'.$row["program"].'</td>
<td class="center">'.$row["cert_no"].'</td>
<td class="center">'.$row["date_ofissue"].'</td>
<td class="center">Picked</td>
</tr>';
}
}
$db->close();
?>
Maybe try by adding some coma and not using * the other, something like this :
SELECT s.studentsregno, s.fullname, s.program, c.certificateno AS cert_no, c.dateofissue AS date_ofissue, c.status AS pick_status FROM student s JOIN certificate c on (c.studentreg = s.studentregno);
I'm not a big fan of renaming like this the table too so if it still doesn't work, try by remplacing s and c by the real table name and just use this surname when you really need it (a join beetween two from the same table)
Long story short, I have an admin section where the user can choose from multiple dropdown lists the tables and fields that must be queries in order to get some values. Therefore, the query in ZEND is performed by concatenating the strings
$query = "SELECT $fieldName1, $fieldName2 from $tableName where $fieldName1 = $value";
How can I escape the above using ZEND approach to avoid sql injection? I tried adding them all as ? and calling quoteinto but it seems this does not work on some of the variables (like table names or field names)
ZF has quoteIdentifier() specifically for this purpose:
$query = "SELECT ".$db->quoteIdentifier($fieldName1).","...
In your case you might (also) want to check against a white list of valid column names.
Use quoteInto() or Zend_db_Select::where() for the values, and for the table and column names, I would simply strip any non alpha characters and then wrap them in ` quotes prior to using them in your SQL.
Example:
// Strip non alpha and quote
$fieldName1 = '`' . preg_replace('/[^A-Za-z]/', '', $fieldName1) . '`';
$tableName = '`' . preg_replace('/[^A-Za-z]/', '', $tableName) . '`';
// ....
// Build the SQL using Zend Db Select
$db->select()->from($tableName, array($fieldName1, $fieldName2))
->where($fieldName1 . ' = ?', $value);
In SafeMysql you can make it as simple, as
$sql = "SELECT ?n, ?n from ?n where ?n = ?s";
$data = $db->getAll($sql,$fieldName1,$fieldName2, $tableName, $fieldName1, $value);
though I understand that you won't change your ZF to SafeMysql.
Nevertheless, there is one essential thing that is ought to be done manually:
I doubt you want to let users to browse users table or financial table or whatever.
So, you have to verify a passed table name against an allowed tables array.
like
$allowed = ('test1','test2');
if (!in_array($tableName, $allowed)) {
throw new _403();
}
Background
Currently I am using DB2 V9 version. One of my stored procedure is taking time to execute. I looked BMC apptune and found the following SQL.
There are three tables we were using to execute the following query.
ACCOUNT table is having 3413 records
EXCHANGE_RATE is having 1267K records
BALANCE is having 113M records
Someone has added recently following piece of code in the query. I think because of this we had a problem.
AND (((A.ACT <> A.EW_ACT)
AND (A.EW_ACT <> ' ')
AND (C.ACT = A.EW_ACT))
OR (C.ACT = A.ACT))
Query
SELECT F1.CLO_LED
INTO :H :H
FROM (SELECT A.ACT, A.BNK, A.ACT_TYPE,
CASE WHEN :H = A.CUY_TYPE THEN DEC(C.CLO_LED, 21, 2)
ELSE DEC(MULTIPLY_ALT(C.CLO_LED, COALESCE(B.EXC_RATE, 0)), 21, 2)
END AS CLO_LED
FROM ACCOUNT A
LEFT OUTER JOIN EXCHANGE_RATE B
ON B.EFF_DATE = CURRENT DATE - 1 DAY
AND B.CURCY_FROM = A.CURNCY_TYPE
AND B.CURCY_TO = :H
AND B.STA_TYPE = 'A'
, BALANCE C
WHERE A.CUSR_ID = :DCL.CUST-ID
AND A.ACT = :DCL.ACT
AND A.EIG_RTN = :WS-BNK-ID
AND A.ACT_TYPE = :DCL.ACT-TYPE
AND A.ACT_CAT = :DCL.ACT-CAT
AND A.STA_TYPE = 'A'
AND (((A.ACT <> A.EW_ACT)
AND (A.EW_ACT <> ' ')
AND (C.ACT = A.EW_ACT))
OR (C.ACT = A.ACT))
AND C.BNK = :WS-BNK-ID
AND C.ACT_TYPE = :DCL.ACT-TYPE
AND C.BUS_DATE = :WS-DATE-FROM) F1
WITH UR
There's a number of wierd things going on in this query. The most twitchy of which is mixing explicit joins with the implicit-join syntax; frankly, I'm not certain how the system interprets it. You also appear to be using the same host-variable for both input and output; please don't.
Also, why are your column names so short? DB2 (that version, at least) supports column names that are much longer. Please save people's sanity, if at all possible.
We can't completely say why things are slow - we may need to see access plans. In the meantime, here's your query, restructured to what may be a faster form:
SELECT CASE WHEN :inputType = a.cuy_type THEN DEC(b.clo_led, 21, 2)
ELSE DEC(MULTIPLY_ALT(b.clo_led, COALESCE(c.exc_rate, 0)), 21, 2) END
INTO :amount :amountIndicator -- if you get results, do you need the indiciator?
FROM Account as a
JOIN Balance as b -- This is assumed to not be a 'left', given coalesce not used
ON b.bnk = a.eig_rtn
AND b.act_type = a.act_type
AND b.bus_date = :ws-date-from
AND ((a.act <> a.ew_act -- something feels wrong here, but
AND a.ew_act <> ' ' -- without knowing the data, I don't
AND c.act = a.ew_act) -- want to muck with it.
OR c.act = a.act)
LEFT JOIN Exchange_Rate as c
ON c.eff_date = current_date - 1 day
AND c.curcy_from = a.curncy_type
AND c.sta_type = a.sta_type
AND c.curcy_to = :destinationCurrency
WHERE a.cusr_id = :dcl.cust-id
AND a.act = :dcl.act
AND a.eig_rtn = :ws-bnk-id
AND a.act_type = :dcl.act-type
AND a.act_cat = :dcl.act-cat
AND a.sta_type = 'A'
WITH UR
FECTCH FIRST 1 ROW ONLY
A few other notes:
Only specify exactly those columns needed - under certain circumstances, this permits index-only access, where otherwise a followup table-access may be needed. However, this probably won't help here.
COALESCE(c.exc_rate, 0) feels off somehow - if no exchange rate is present, you return an amount of 0, which could otherwise be a valid amount. You may need to return some sort of indicator, or make it a normal join, not an outer one.
Also, try both this version, and possibly a version where host variables are specified in addition to the conditions between tables. The optimizer should be able to automatically commute the values, but may not under some conditions (implementation detail).