Best way to exclude records from multiple tables - oracle

I got the following tables (just an example): vehicles, vehicle_descriptions, vehicle_parts
vehicles have 1 to many with vehicle_descriptions and vehicle_parts. There may not be a corresponding vehicle_description/part for a given vehicle.
SELECT * FROM vehicles
LEFT OUTER JOIN vehicles d ON vehicles.vin = d.vin AND d.summary NOT LIKE 'honda'
LEFT OUTER JOIN
(SELECT SUM(desc_total) FROM vehicle_descriptions WHERE NOT LIKE desc 'honda' GROUP BY vin) b
ON vehicles.vin = vehicle_b.vin
LEFT OUTER JOIN
(SELECT SUM(part_count) FROM vehicle_parts WHERE part_for NOT LIKE 'honda' GROUP BY vin) c ON vehicles.vin = c.vin
If either vehicle_desc, vehicles, or part contains the exclusion term, the whole record should not show up in the result set. The query above will return a record even if one of the tables contain the exclusion term Honda. How would I fix the above query?

You're not using any of the information in either sum() as part of what you show, just to decide whether to include the vehicle. And you're doing an unnecessary self join in your first clause. Generally in situations like this, the "exists" and "not exists" clauses work well. So what about this? I'll use Oracle syntax, you can convert to ANSI of course.
SELECT * FROM vehicles v where summary <> 'honda'
and not exists (select 1 from vehicle_descriptions d where d.vin = v.vin and d.desc <> 'honda')
and not exists (select 1 from vehicle_parts p where p.vin = v.vin and p.part_for <> 'honda')

Related

How to fix this Sql code to give the names of student and their grades who got better grades than the highest grade by gray in classes taught by KEEN

I have 4 tables which are class, student, faculty, and grade.
I want SQL to give the names of students and their grades who got better grades than the highest grade by GRAY in classes taught by KEEN.
Here is a sample of what I did
SELECT S_NAME, GRADE
FROM GRADE1
WHERE GRADE >(SELECT GRADE FROM GRADE1 G
INNER JOIN CLASS1 C ON G.C_NAME=C.C_NAME
INNER JOIN aggarwal.FACULTY1 F ON F.F_NAME
WHERE G.S_NAME='GRAY' AND F.F_NAME='KEEN');
ERROR at line 6:
ORA-00920: invalid relational operator
Any help as to how?
The last INNER JOIN is a culprit; its ON clause doesn't contain everything it should - what are you joining F.F_NAME to?
Besides, a few more objections:
always use table aliases, especially when working with subqueries because you might find yourself puzzled getting result you didn't expect
subquery misses the MAX function; generally speaking, you should take care that it returns a single value, otherwise you'll get TOO-MANY-ROWS, unless you apply ALL (or ANY), e.g.
where r.grade > any (select g.grade from grade1 g ...)
why did you precede table name with its ownere, here: aggarwal.FACULTY1? Don't all of your tables belong to the same schema? If so, remove it (or, for consistency, use it with all other tables as well).
Finally, your query might look like this:
select r.s_name, r.grade
from grade1 r --> missing alias(es)
where r.grade > (select max(g.grade) --> missing MAX
from grade1 g
inner join class1 c on g.c_name = c.c_name
inner join faculty1 f on f.f_name = ... --> missing value here
where g.s_name = 'GRAY'
and f.f_name = 'KEEN'
);

sql select records that don't have relation in a third table

I have three tables
CLAIMS_TB
CLAIMS_RISK_TB
VEHICLE_TB
And then I need this result below:
Who can help me or share with me the query to be used?
N.B: If the code is 700 it means that it is a vehicle and it must fill the column called "ai_vehicle_use" otherwise it must leave it blank because "VEHICLE_TB" table contains only vehicles
This is what I tried:
select
klm.CM_PL_INDEX,
klm.cm_no,
klmrisk.cr_risk_code,
CASE WHEN klm.CM_PR_CODE = '0700' THEN klmrisk.cr_risk_code ELSE '' END,
veh.ai_vehicle_use
from CLAIMS_TB klm
JOIN CLAIMS_RISK_TB klmrisk
ON (klm.cm_index = klmrisk.cr_cm_index)
INNER JOIN VEHICLE_TB veh
on veh.ai_regn_no = klm.cm_no
where klm.cm_no='CL/01/044/00001/01/2018'
or klmrisk.cr_cm_index='86594'
order by klmrisk.cr_risk_code;
I believe this could fit your needs.
SELECT
*
FROM CLAIMS_TB AS c
LEFT JOIN CLAIMS_RISK_TB cl ON c.cm_index = cl.cr_cm_index
LEFT JOIN VEHICLE_TB v ON cl.cr_risk_code = v.ai_risk_index
Finaly I find the solution, query below works:
select * from CLAIMS_TB c
JOIN CLAIMS_RISK_TB cr ON( C.CM_INDEX = cr.cr_cm_index)
LEFT OUTER JOIN VEHICLE_TB v ON (cr.cr_risk_code = v.ai_regn_no);

How to simplify this postgres query

How can I simplify this query?
What I am trying to do is derive the column S9_Unlock via a subquery in which I only look for user_ids which are returned from the main query but this looks very awkward to me, especially as this query here is just an excerpt. In reality I am doing multiple of these subqueries to derive different columns...
SELECT userid, CAST(to_char(S9_unlock,'YYYY/MM/DD') AS timestamp) AS "S9_Unlock"
FROM (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON inv.id = ca.invoice_id
LEFT JOIN shop_db.cart_items AS ci ON ca.id = ci.cart_id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
GROUP BY user_id) AS master
LEFT JOIN (
SELECT MIN(s3.unl) AS "S9_Unlock", s3.user_id
FROM (
SELECT user_id, challenge_codes.created AS unl,
MAX /* Check if license contains Suite9 */
(CASE WHEN substring(bundle_article_code,1,6) = 'BuSu90' THEN 1 ELSE 0 END) AS "S9_Unlock"
FROM licensing_db.serial_numbers
LEFT JOIN licensing_db.licenses ON licenses.id = serial_numbers.license_id
LEFT JOIN user_db.users ON users.id = licenses.user_id
LEFT JOIN licensing_db.challenge_codes ON challenge_codes.serial_number_id = serial_numbers.id
WHERE user_id IN (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON inv.id = ca.invoice_id
LEFT JOIN shop_db.cart_items AS ci ON ca.id = ci.cart_id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
GROUP BY user_id
)
GROUP BY user_id, challenge_codes.created) AS s3
)
WHERE "S9_Unlock" = 1
AND s3.unl IS NOT NULL
GROUP BY s3.user_id) AS "S9_Unlock" ON "S9_Unlock".user_id = master.userid
In your query you have two sub-queries that are identical; this screams for a CTE.
In the sub-query on licensing issues you can filter out the valid licenses after the GROUP BY clause using a HAVING clause. Make that a WITH QUERY too and you end up with the rather more readable:
WITH inv AS (
SELECT ca.user_id AS userid
FROM shop_db.invoices AS inv
LEFT JOIN shop_db.carts AS ca ON ca.invoice_id = inv.id
LEFT JOIN shop_db.cart_items AS ci ON ci.cart_id = ca.id
WHERE (inv.created BETWEEN '2014-11-13' AND '2014-11-14' OR inv.created BETWEEN '2013-11-14' AND '2013-11-15')
AND inv.status <> 'do_not_book'
AND inv.id IS NOT NULL
), s3 AS (
SELECT u.user_id, min(cc.created) AS first_unlocked, bundle_article_code
FROM licensing_db.serial_numbers AS sn
LEFT JOIN licensing_db.licenses AS lic ON lic.id = sn.license_id
LEFT JOIN user_db.users AS u ON u.id = lic.user_id
LEFT JOIN licensing_db.challenge_codes AS cc ON cc.serial_number_id = sn.id
WHERE u.user_id IN (SELECT userid FROM inv)
GROUP BY u.user_id, bundle_article_code
HAVING bundle_article_code LIKE 'BuSu90%'
AND first_unlocked IS NOT NULL
)
SELECT userid, date_trunc('day', first_unlocked) AS "S9_Unlock"
FROM inv
LEFT JOIN s3 ON s3.user_id = inv.userid;
So the main query is now reduced to 3 lines and both the WITH-QUERY's perform a logically self-contained query of the database. The other sub-queries you refer to can similarly become a WITH-QUERY and then you assemble them in the main query. Remember that you can refer to earlier named queries in the list of with-queries, as is shown above with inv being referred to by s3. While such CTE's are syntactically not providing new functionality (except for the RECURSIVE variant), they do make complex queries much more readable and therefore easier to maintain.
Another approach would be to factor out logical sub-components (such as the inv sub-query) and make a VIEW out of those. Then you can simply reference the view in the main query. Making the whole thing a view is probably also a good idea if you want to make the query more flexible. What if you want to query for Suite9.1 ('BuSu91%') on 27 March 2014? Taken those literals out and then using them as WHERE clauses in a view makes your query more versatile; this can be either with sub-queries or with the complete CTE.
(Please check if the semantics are still right in the s3 with-query because without your table structures and sample data I ccannot test my code above.)
Instead of solving your problem as one big monolithic relational sql query, I would seriously consider going the "procedural" way, by using the built-in "plpgsql" language of postgresql. This could bring a lot of clarity in your application.

Hibernate HQL GroupBy in Oracle

I created this query using HQL with Hibernate and Oracle
select c from Cat c
left join c.kittens k
where (c.location= 1 OR c.location = 2)
and (i.activo = 1)
group
by c.id,
c.name,
c.fulldescription,
c.kittens
order by count(e) desc
The problem comes with the fact that in HQL you need to specify all fields in Cat in order to perform a Group By, but fulldescription is a CLOB, and you cannot group by by a CLOB (I get a "Not a Group By Expression" error. I've seen a few solutions around for a pure SQL sentence but none for HQL.
A serious issue GROUP BY of HQL because if you specify your object in GROUP BY and in your SELECT field list behaviours are differents. In GROUP BY has considered only id field but in SELECT field list all fields are considered.
So you can use a subquery with GROUP BY to return only id from your object, so that result becomes an input for the main query, like the follow I write for you.
Pay attention there are some alias table (i and e) not defined, so this query doesn't work, but you know as fixed.
Try this:
select c2 from Cat c2
where c2.id in (
select c.id from Cat c
left join c.kittens k
where (c.location= 1 OR c.location = 2)
and (i.activo = 1) <-- who is i alias??
group by c.id)
order by count(e) desc <-- who is e alias???

Cognos 8 Query to find all Report and Column Names

I want to query the meta data in Cognos 8 to find all report and column names. If possible, I'd like to include the column definitions.
Can I do this using a Cognos report or do I need to query some repository?
Thanks.
You can select a list of reports from the content store with the following query:
SELECT CMOBJNAMES_BASE.NAME AS ObjName, CMOBJECTS.PCMID, CMCLASSES.NAME AS ClassName, CMOBJPROPS7.spec
FROM CMOBJECTS
JOIN CMOBJNAMES_BASE ON CMOBJECTS.CMID = CMOBJNAMES_BASE.CMID
JOIN CMCLASSES ON CMOBJECTS.CLASSID = CMCLASSES.CLASSID
LEFT JOIN CMOBJPROPS7 ON CMOBJECTS.CMID = CMOBJPROPS7.CMID
WHERE CMOBJECTS.CLASSID IN (10, 37)
ORDER BY CMOBJECTS.PCMID;
I use that in Cognos 10. I believe in cognos 8 the CMOBJNAMES_BASE table is actually named 'CMOBJNAMES' without the _BASE.
UPDATE: Has been tested and works in Cognos 11r9.
The Report metadata is stored in the 'SPEC' column of CMOBJPROPS7 as XML. You can parse this XML in order to strip out the columns used in the report. It will not be a simple task.
If you have time but not money, you can write your own code to parse that XML. If you have more money than time, you can buy a 3rd party program to accomplish this, such as Motio or BSP Metamanager.
The query above is less useful for building a clean list of columns, but great for searching for specific data items. For example, you have column you are wanting to change in a data source, but you are not sure which report uses that column. Run the query above, and search for the data item. It will be embedded within the XML in the Cognos MDX format, ie. [Presentation View].[Sales Summary].[Sales]
EDIT: As requested below, here is a query that includes folder paths.
-- List of Reports, the folder they are in, and the package they are using
select distinct temp2.name as package,temp1.folder,temp1.name from
(SELECT temp.PARENTNAME AS FOLDER,CMOBJECTS.PCMID,CMOBJNAMES.CMID, CMOBJNAMES.LOCALEID, CMOBJNAMES.MAPDLOCALEID, CMOBJNAMES.ISDEFAULT, CMOBJNAMES.NAME,
CMOBJECTS.CLASSID
FROM CMOBJNAMES INNER JOIN
CMOBJECTS ON CMOBJNAMES.CMID = CMOBJECTS.CMID
INNER JOIN
(SELECT P.CMID AS PARENT,P.NAME AS PARENTNAME FROM CMOBJNAMES P where P.LOCALEID between 24 and 52) temp
ON CMOBJECTS.PCMID = TEMP.PARENT
WHERE (CMOBJECTS.CLASSID = 10)
AND SUBSTR(TEMP.PARENTNAME,1,1) NOT IN ('1','2','3','4','5','6','7','8','9') AND
TEMP.PARENTNAME NOT LIKE 'Backup%') temp1
inner join
(SELECT CMREFNOORD1.CMID AS PID, CMREFNOORD1.REFCMID, CMOBJNAMES.NAME
FROM CMREFNOORD1 INNER JOIN
CMOBJECTS ON CMREFNOORD1.REFCMID = CMOBJECTS.CMID INNER JOIN
CMOBJNAMES ON CMOBJECTS.CMID = CMOBJNAMES.CMID
WHERE (CMREFNOORD1.PROPID = 31 AND CMOBJNAMES.LOCALEID between 24 and 52)) temp2
on temp1.cmid = temp2.pid and LOCALEID between 24 and 52;
Not sure if this will help anybody, but our version doesn't have a table named CMOBJNAMES_BASE.
This is what works for me:
select ob2.cmid, c.name as classname, n.name as objectname, o.DELIVOPTIONS as deliveryoptions, z2.name as owner
from CMOBJPROPS2 p
inner join CMOBJPROPS26 o on p.cmid=o.cmid
inner join CMOBJECTS ob on ob.cmid=o.cmid
inner join CMOBJECTS ob2 on ob.pcmid=ob2.cmid
inner join CMOBJNAMES n on n.cmid=ob2.cmid
inner join CMCLASSES c on ob2.classid=c.classid
left join CMREFNOORD2 z1 on z1.cmid = p.cmid
left join CMOBJPROPS33 z2 on z2.CMID = z1.REFCMID
where ACTIVE = 1 order by z2.name, objectName

Resources