No more spool space in Teradata while trying Update - teradata

I'm trying to update a table with to many rows 388.000.
This is the query:
update DL_RG_ANALYTICS.SH_historico
from
(
SELECT
CAST((MAX_DIA - DIA_PAGO) AS INTEGER) AS DIAS_AL_CIERRE_1
FROM
(SELECT * FROM DL_RG_ANALYTICS.SH_historico A
LEFT JOIN
(SELECT ANO||MES AS ANO_MES, MAX(DIA) AS MAX_DIA FROM DL_RG_ANALYTICS.SH_CALENDARIO
GROUP BY 1) B
ON A.ANOMES = B.ANO_MES
) M) N
SET DIAS_AL_CIERRE = DIAS_AL_CIERRE_1;
Any help is apreciate.

This first thing I'd do is replace the SELECT * with only the columns you need. You can also remove the M derived table to make it easier to read:
UPDATE DL_RG_ANALYTICS.SH_historico
FROM (
SELECT CAST((MAX_DIA - DIA_PAGO) AS INTEGER) AS DIAS_AL_CIERRE_1
FROM DL_RG_ANALYTICS.SH_historico A
LEFT JOIN (
SELECT ANO || MES AS ANO_MES, MAX(DIA) AS MAX_DIA
FROM DL_RG_ANALYTICS.SH_CALENDARIO
GROUP BY 1
) B ON A.ANOMES = B.ANO_MES
) N
SET DIAS_AL_CIERRE = DIAS_AL_CIERRE_1;
What indexes are defined on the SH_CALENDARIO table? If there is a composite index of (ANO, MES) then you should re-write your LEFT JOIN sub-query to GROUP BY these two columns since you concatenate them together anyways. In general, you want to perform joins, GROUP BY and OLAP functions on indexes, so there will be less row re-distribution and they will run more efficiently.
Also, this query is updating all rows in the table with the same value. Is this intended, or do you want to include extra columns in your WHERE clause?

Related

Is it possible to compare value to multiple columns in ''In'' clause?

select m.value
from MY_TABLE m
where m.value in (select m2.some_third_value, m2.some_fourth_value
from MY_TABLE_2 m2
where m2.first_val member of v_my_array
or m2.second_val member of v_my_array_2)
Is it possible to write a select similar to this, where m.value is compared to two columns and has to match at least one of those? Something like where m.value in (select m2.first_val, m2.second_val). Or is writing two separate selects unavoidable here?
No. When there are multiple columns in the IN clause, there must be the same number of columns in the WHERE clause. The pairwise query compares each record in the WHERE clause against the records returned by the sub-query. The statement below
SELECT *
FROM table_main m
WHERE ( m.col_1, m.col_2 ) IN (SELECT s.col_a,
s.col_b
FROM table_sub s)
is equivalent to
SELECT *
FROM table_main m
WHERE EXISTS (SELECT 1
FROM table_sub s
WHERE m.col_1 = s.col_a
AND m.col_2 = s.col_b)
The only way to search both columns in one SELECT statement would be to OUTER JOIN the second table to the first table.
SELECT m.*
FROM table_main m
LEFT JOIN table_sub s ON (m.col_1 = s.col_a OR m.col_1 = s.col_b)
WHERE m.col_1 = s.col_a
OR m.col_1 = s.col_b

order of search for Sqlite's "IN" operator guaranteed?

I'm performing an Sqlite3 query similar to
SELECT * FROM nodes WHERE name IN ('name1', 'name2', 'name3', ...) LIMIT 1
Am I guaranteed that it will search for name1 first, name2 second, etc? Such that by limiting my output to 1 I know that I found the first hit according to my ordering of items in the IN clause?
Update: with some testing it seems to always return the first hit in the index regardless of the IN order. It's using the order of the index on name. Is there some way to enforce the search order?
The order of the returned rows is not guaranteed to match the order of the items inside the parenthesis after IN.
What you can do is use ORDER BY in your statement with the use of the function INSTR():
SELECT * FROM nodes
WHERE name IN ('name1', 'name2', 'name3')
ORDER BY INSTR(',name1,name2,name3,', ',' || name || ',')
LIMIT 1
This code uses the same list from the IN clause as a string, where the items are in the same order, concatenated and separated by commas, assuming that the items do not contain commas.
This way the results are ordered by their position in the list and then LIMIT 1 will return the 1st of them which is closer to the start of the list.
Another way to achieve the same results is by using a CTE which returns the list along with an Id which serves as the desired ordering of the results, which will be joined to the table:
WITH list(id, item) AS (
SELECT 1, 'name1' UNION ALL
SELECT 2, 'name2' UNION ALL
SELECT 3, 'name3'
)
SELECT n.*
FROM nodes n INNER JOIN list l
ON l.item = n.name
ORDER BY l.id
LIMIT 1
Or:
WITH list(id, item) AS (
SELECT * FROM (VALUES
(1, 'name1'), (2, 'name2'), (3, 'name3')
)
)
SELECT n.*
FROM nodes n INNER JOIN list l
ON l.item = n.name
ORDER BY l.id
LIMIT 1
This way you don't have to repeat the list twice.

How to delete multiple rows with 2 columns as composite primary key in SQLite?

I need to delete some rows in a SQLite table with two columns as primary key, like this:
DELETE FROM apt_lang
WHERE (apt_fk, apt_lang_fk) NOT IN ((42122,"en"),(42123,"es"),(42123,"en"))
This works on Oracle and MySQL but not in SQLite.
Can anybody help me?
First, find out which rows you want to delete.
The easiest way is with a join:
SELECT *
FROM apt_lang
JOIN (SELECT 42122 AS apt_fk, 'en' AS apt_lang_fk UNION ALL
SELECT 42123 , 'es' UNION ALL
SELECT 42123 , 'en' )
USING (apt_fk, apt_lang_fk)
To use this with a DELTE, either check with EXISTS for a match:
DELETE FROM apt_lang
WHERE NOT EXISTS (SELECT 1
FROM apt_lang AS a2
JOIN (SELECT 42122 AS apt_fk, 'en' AS apt_lang_fk UNION ALL
SELECT 42123 , 'es' UNION ALL
SELECT 42123 , 'en' )
USING (apt_fk, apt_lang_fk)
WHERE apt_fk = apt_lang.apt_fk
AND apt_lang_fk = apt_lang.apt_lang_fk)
or get the ROWIDs of the subquery and check against those:
DELETE FROM apt_lang
WHERE rowid NOT IN (SELECT apt_lang.rowid
FROM apt_lang
JOIN (SELECT 42122 AS apt_fk, 'en' AS apt_lang_fk UNION ALL
SELECT 42123 , 'es' UNION ALL
SELECT 42123 , 'en' )
USING (apt_fk, apt_lang_fk))
This should work:
DELETE FROM apt_lang WHERE (apt_fk, apt_lang_fk) NOT IN (VALUES (42122,"en"),(42123,"es"),(42123,"en"))
Yes, it's possible to delete rows from SQLite based on a subquery that builds on multiple columns. This can be done with SQLite's concatenate "||" operator. It might help to show an example.
Setup:
create table a (x,y);
insert into a values ('A','B');
insert into a values ('A','C');
create table b (x,y);
insert into b values ('A','C');
insert into b values ('A','X');
Show Tables:
select * from a;
A|B
A|C
select * from b;
A|C
A|X
Assuming you want to delete from table a rows where column x and column y don't match with table b, the following select will accomplish that.
delete from a where x||y not in (select a.x||a.y from a,b where a.x=b.x and a.y=b.y);
Result:
select * from a;
A|B
Summary
This relies on concatenating several columns into one with the "||" operator. Note, it will work on calculated values too, but it might require casting the values. So, just a few conversions to note with the "||" operator...
select 9+12|| 'test';
21 -- Note we lost 'test'
select cast(9+12 as text)|| 'test';
21test -- Good! 'test' is there.

Drupal db_query get all rows

I'm starting to build themes for Drupal.
I have a question about drupal queries, but I didn't find any answer...
I have a db_query in one of my modules, but only return a few rows (13 rows always, and table has 20) How can I select all rows from one table?
My query is like this
$result = db_query("SELECT * FROM bv_calendar c INNER JOIN bv_countries p ON c.country_id = p.country_id INNER JOIN bv_vaccinate v ON c.vaccinate_id = v.vaccinate_id ORDER BY $orderby ASC");
while ($class = $result->fetchAssoc()) {
$classes[$class["calendar_id"]] = $class;
}
How can I get all rows?
Thank you
From the looks of it I'd say you're not getting all the rows from the *bv_calendar* because you're using INNER JOIN on your SQL query which requires to be matching records on your *bv_countries* and *bv_vaccinate* tables.
Try replacing the INNER JOIN for LEFT JOIN as in the later "If there is no matching row for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table." from http://dev.mysql.com/doc/refman/5.0/en/join.html.
If that's the result you want then your SQL query should be:
SELECT * FROM bv_calendar c LEFT JOIN bv_countries p ON c.country_id = p.country_id LEFT JOIN bv_vaccinate v ON c.vaccinate_id = v.vaccinate_id ORDER BY $orderby ASC

record types that weren't found for a specific value in oracle query

I have this query
Select distinct p_id, p_date,p_city
from p_master
where p_a_id in(1,2,5,8,2,1,10,02)
and my IN clause contains 200 values. How do I get to know which ones weren't returned by the query. Each value in the IN clause may have a record in some cases they don't. I want to know all the records that weren't found for any selected p_a_id type.
Please help
This will do the trick but I'm sure there's an easier way to find this out :-)
with test1 as
(select '1,2,5,8,2,1,10,02' str from dual)
select * from (
select trim(x.column_value.extract('e/text()')) cols
from test1 t, table (xmlsequence(xmltype('<e><e>' || replace(t.str,',','</e><e>')|| '</e></e>').extract('e/e'))) x) cols
left outer join
(Select count(*), p_a_id from p_master where p_a_id in (1,2,5,8,2,1,10,02) group by p_a_id) p
on p.p_a_id = cols.cols
where p_a_id is null
;

Resources