SQLite: Table alias not found when trying to create a new table from a join of two select statements - sqlite

I hope the title makes sense, I'm fairly new to writing queries and am shaky on the terms and such.
I'd like some help understanding why my table alias isn't found on the following query. When I run it without the ON clause it executes (on SQLite Manager on Firefox) but with the clause, I get the error 'no such column t2.story_id'
NOTE - I'm using some dummy queries I created in order to simplify the problem. My final query will be structured exactly the same, so if you see any errors or things I can improve on please share. In case anyone is wondering why the two statements are pulling from the same table it's because I'll need to join two statements that pull from the same table but do very specific things that can't be done at the same time. At least I was told this was the way to do it haha.
SELECT * FROM
(
SELECT * FROM
(
SELECT story_id, role_type, user_id
FROM cur_cycle_role_activity
) t1 /* end of the first inner select statement t1 */
LEFT JOIN /* Begin Join Statement */
(
SELECT * FROM
(
SELECT story_id, workstream_num FROM cur_cycle_role_activity
) t2 /* end of the second inner select statement t2 */
) /* End Join */
ON t1.story_id = t2.story_id /* On clause for Join above */
) /* This is the end of the topmost select statement */
Error message:
[ no such column: t2.story_id ]

Wrapping your queries in SELECT * FROM (...) is causing the problem, because you can't refer to aliases in a subquery.
SELECT t1.story_id, t1.role_type, t1.user_id, t2.workstream_num
FROM (
SELECT story_id, role_type, user_id
FROM cur_cycle_role_activity) AS t1
LEFT JOIN (
SELECT story_id, workstream_num
FROM cur_cycle_role_activity) AS t2
ON t1.story_id = t2.story_id

Related

Missing row from MariaDB result set when constants used twice - once inside derived table and once outside

Why is only 1 row returned when 2 definitely exist?
Here is the query I am trying to run:
select t.term_id, t.name, t.slug, a.c, a.term_order, a.menu_order, ttparent.taxonomy from
(
SELECT p.term_id, count(distinct p.ID) c, p.term_order, p.menu_order
FROM pz_fww_object_ancestors p
WHERE p.taxonomy = 'product_cat' and p.term_id IN (1445,9561) group by term_id
) a inner join pz_terms t
ON a.term_id = t.term_id inner join pz_term_taxonomy ttparent on ttparent.term_id = t.term_id
and t.term_id IN (1445, 9561)
;
There should be 2 rows returned, but only 1 row is returned - the one for 1445.
However - this row DEFINITELY should be returnable. If I change the final line of the query to term_id in (9561) then it does return the other row.
One workaround I can do is to remove this extra check of these constants against the term_id, but I'm aware that the MySQL optimizer will not infer or pass through constants from derived tables and so there could be potential in some cases for the optimizer to run this query slowly if I don't also have these constants at the top level of the query rather than just on the derived table.
Here's the 2 result set with the constants removed from the outer/top-level query:
I've looked at the EXPLAIN and yes, the queries run in a different order when this additional constant check exists at the top level, so I presume this is where the bug exists.
Here's the explain from the working query, without the constants added to the top level query:
In case it matters, this is all with MariaDB version 10.3.31-MariaDB-0ubuntu0.20.04.1.

string_agg function with IN operator not working in PostgreSQL Query

here is my query
select *
from table
where id in ( select string_agg(CAST(id as varchar), '","') FROM table)
string_agg() is completely useless and unnecessary for that:
select *
from table_one
where id in (select id FROM other_table)
I assume you are doing that for two different tables, otherwise that would be a very expensive way of writing: select * from table where id is not null

CREATE TABLE with CTE statement

Is it possible to create a table from a query with a CTE statement?
Something like:
CREATE TABLE db1.test1 AS
(
WITH cte1(v1) as
( SEL v1 FROM db1.table1 )
SEL * FROM cte1
)
This is how the CTE's look like:
WITH employees(id, name, boss, senior_boss) AS
(
SEL
empls.id,
empls.name,
supervisors.name as boss,
senior_bosses.name as senior_boss
FROM empl_cte AS empls
LEFT JOIN empl_cte AS supervisors ON empls.boss_id = supervisors.id
LEFT JOIN empl_cte AS senior_bosses ON supervisors.boss_id = senior_bosses.id
),
WITH empl_cte(....) AS
(
SEL
id,
name
boss_id
FROM all_employees
WHERE <some_filters>
)
SEL
*
FROM products
LEFT JOIN employees ON products.sales_rep_id = employees.id
Both
converting the CTEs into views
and
converting employees as a sub-query (empl_cte as a VIEW) in the left join
leads to a massive loss of performance (run time blowing up from a couple of minutes to days of work). I can't figure out how Teradata optimizer works.
EXPLAIN on the new refactored queries seem indicate that the LEFT JOIN becomes a product join draining countless of time.
This will work in V16 (and possibly earlier versions).
CREATE TABLE myTable AS (
SELECT * FROM (
WITH x AS (
SELECT ...
FROM ...
WHERE ...
)
SELECT ...
FROM x ...
WHERE ...
) D
) WITH DATA PRIMARY INDEX (PK)
;
Basically you need to wrap the whole query, including the CTE, in a SELECT with an alias.

PL/SQL - comma separated list within IN CLAUSE

I am having trouble getting a block of pl/sql code to work. In the top of my procedure I get some data from my oracle apex application on what checkboxes are checked. Because the report that contains the checkboxes is generated dynamically I have to loop through the
APEX_APPLICATION.G_F01
list and generate a comma separated string which looks like this
v_list VARCHAR2(255) := (1,3,5,9,10);
I want to then query on that list later and place the v_list on an IN clause like so
SELECT * FROM users
WHERE user_id IN (v_list);
This of course throws an error. My question is what can I convert the v_list to in order to be able to insert it into a IN clause in a query within a pl/sql procedure?
If users is small and user_id doesn't contain commas, you could use:
SELECT * FROM users WHERE ',' || v_list || ',' LIKE '%,'||user_id||',%'
This query is not optimal though because it can't use indexes on user_id.
I advise you to use a pipelined function that returns a table of NUMBER that you can query directly. For example:
CREATE TYPE tab_number IS TABLE OF NUMBER;
/
CREATE OR REPLACE FUNCTION string_to_table_num(p VARCHAR2)
RETURN tab_number
PIPELINED IS
BEGIN
FOR cc IN (SELECT rtrim(regexp_substr(str, '[^,]*,', 1, level), ',') res
FROM (SELECT p || ',' str FROM dual)
CONNECT BY level <= length(str)
- length(replace(str, ',', ''))) LOOP
PIPE ROW(cc.res);
END LOOP;
END;
/
You would then be able to build queries such as:
SELECT *
FROM users
WHERE user_id IN (SELECT *
FROM TABLE(string_to_table_num('1,2,3,4,5'));
You can use XMLTABLE as follows
SELECT * FROM users
WHERE user_id IN (SELECT to_number(column_value) FROM XMLTABLE(v_list));
I have tried to find a solution for that too but never succeeded. You can build the query as a string and then run EXECUTE IMMEDIATE, see http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm#i14500.
That said, it just occurred to me that the argument of an IN clause can be a sub-select:
SELECT * FROM users
WHERE user_id IN (SELECT something FROM somewhere)
so, is it possible to expose the checkbox values as a stored function? Then you might be able to do something like
SELECT * FROM users
WHERE user_id IN (SELECT my_package.checkbox_func FROM dual)
Personally, i like this approach:
with t as (select 'a,b,c,d,e' str from dual)
--
select val
from t, xmltable('/root/e/text()'
passing xmltype('<root><e>' || replace(t.str,',','</e><e>')|| '</e></root>')
columns val varchar2(10) path '/'
)
Which can be found among other examples in Thread: Split Comma Delimited String Oracle
If you feel like swamping in even more options, visit the OTN plsql forums.

How to generate columns dynamically?

I have table with certain number of columns.
I want to populate other table with the data of a particular column of Table1 as columns of table2 dynamically.
When I say dynamically I mean to say that when ever any data is added to the column of Table1 the table2 is populated with as many number of columns.
Changing the schema on the fly really isn't a good idea, for a number of reasons. From what you've described, I think you would be better off using a view for this. A view will give you the dynamic capabilities you're looking for with fewer side effects.
See this article:
How to create a view in SQL Server
I will once again repeat the disclaimer that this is a bad idea, many things can go wrong, and I'm certain there is a better solution to whatever underlying problem you're trying to solve. That said, to answer the explicit question anyway, here is an example of how to do this:
USE tempdb;
GO
CREATE TABLE dbo.Table1(Description VARCHAR(32));
CREATE TABLE dbo.Table2(ID INT);
GO
CREATE TRIGGER dbo.CatchNewTable1Data
ON dbo.Table1
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) +
'ALTER TABLE dbo.Table2 ADD '
+ QUOTENAME(d) + ' VARCHAR(255);' -- guessing on destination data type
FROM
(
SELECT DISTINCT d = LEFT([Description], 128) -- identifier <= 128
FROM inserted AS i
WHERE NOT EXISTS
(
SELECT 1 FROM sys.columns
WHERE name = LEFT(i.[Description], 128)
AND [object_id] = OBJECT_ID('dbo.Table2')
)
) AS x;
EXEC sp_executesql #sql;
END
GO
Now, let's try it out! Try a column that already exists, a multi-row insert where one of the columns already exists, a multi-row insert with dupes, etc. I am not posting a value > 255 nor am I dealing with any fancy characters that will cause a problem. Why? Because ultimately I don't want you to use this solution, I want to solve the real problem. But for the googlers I want to show that there is a solution to the stated problem.
-- does nothing:
INSERT dbo.Table1 SELECT 'ID';
-- only adds column 'foo':
INSERT dbo.Table1 SELECT 'ID'
UNION ALL SELECT 'foo';
-- adds both of these columns:
INSERT dbo.Table1 SELECT 'bar'
UNION ALL SELECT 'splan foob';
-- only adds one of these:
INSERT dbo.Table1 SELECT 'blat'
UNION ALL SELECT 'blat';
SELECT * FROM dbo.Table2;
Results:
ID foo bar splan foob blat
----------- ------------ ------------ ------------ ------------
Don't forget to clean up:
DROP TABLE dbo.Table1, dbo.Table2;

Resources