In SQL Server, I would this query to define, set and use a variable:
declare #teacherId bigint -- declaring
select #teacherId = Id from Teachers where [Name] = 'John' -- filling/setting
select * from Students where TeacherId = #teacherId -- using
How can I write that in MariaDB? I'm stuck at it. I constantly get errors and the docs is extremely unhelpful and lacks examples. I tried:
declare #teacherId
select Id into #teacherId from Teachers where `Name` = 'John'
But it has errors.
Update:
The error is:
Error in query (1064): Syntax error near 'declare #teacherId select Id into #teacherId from Teachers where Name ...' at line 1
You don't need variables to do that. Join the tables
select *
from Students
join Teachers
on Students.TeacherID = Students.TeacherID
where Teachers.Name= 'John'
MariaDB does have stored procedures, where you can use variables. Please see their documentation.
https://mariadb.com/kb/en/stored-routines/
Related
Im trying to write a recursive query for a use on a old and poorly designed database - and so the queries get quite complex.
Here is the (relevant) table relationships
Because people asked - here is the creation code for these tables:
CREATE TABLE CircuitLayout(
CircuitLayoutID int,
PRIMARY KEY (CircuitLayoutID)
);
CREATE TABLE LitCircuit (
LitCircuitID int,
CircuitLayoutID int,
PRIMARY KEY (LitCircuitID)
FOREIGN KEY (CircuitLayoutID) REFERENCES CircuitLayout(CircuitLayoutID)
);
CREATE TABLE CircuitLayoutItem(
CircuitLayoutItemID int,
CircuitLayoutID int,
TableName varchar(255),
TablePK int,
PRIMARY KEY (CircuitLayoutItemID)
FOREIGN KEY (CircuitLayoutID) REFERENCES CircuitLayout(CircuitLayoutID)
);
TableName refers to another table in the database and thus TablePK is a primary key from the specified table
One of the valid options for TableName is LitCircuit
I'm trying to write a query that will select a circuit and any circuit it is related to
I am having trouble understanding the syntax for recursive ctes
my non-functional attempt is this:
WITH RECURSIVE carries AS (
SELECT LitCircuit.LitCircuitID AS recurseList FROM LitCircuit
JOIN CircuitLayoutItem ON LitCircuit.CircuitLayoutID = CircuitLayoutItem.CircuitLayoutID
WHERE CircuitLayoutItem.TableName = "LitCircuit" AND CircuitLayoutItem.TablePK IN (00340)
UNION
SELECT LitCircuit.LitCircuitID AS CircuitIDs FROM LitCircuit
JOIN CircuitLayout ON LitCircuit.CircuitLayoutID = CircuitLayoutItem.CircuitLayoutID
WHERE CircuitLayoutItem.TableName = "LitCircuit" AND CircuitLayoutItem.TablePK IN (SELECT recurseList FROM carries)
)
SELECT * FROM carries;
the "00340" is a dummy number for testing, and it would get replaced with an actual list in usage
What i'm attempting to do is get a list of LitCircuitIDs based on one or many LitCircuitIDs - that's the anchor member, and that works fine.
What I want to do is take this result and feed it back into itself.
I lack an understanding of how to access data from the anchor member:
I don't know if it is a table with the columns from the select in the anchor or if it is simply a list of resulting values
I dont understand if or where I need to include "carries" in the FROM part of a query
If I were to write this function in python I would do it like this:
def get_circuits(circuit_list):
result_list = []
for layout_item_key, layout_item in CircuitLayoutItem.items():
if layout_item['TableName'] == "LitCircuit" and layout_item['TablePK'] in circuit_list:
layout = layout_item['CircuitLayoutID']
for circuit_key, circuit in LitCircuit.items():
if circuit["CircuitLayoutID"] == layout:
result_list.append(circuit_key)
result_list.extend(get_circuits(result_list))
return result_list
How do I express this in SQL?
danblack's comment made me realize something I was missing:
Here is what I was trying to do:
WITH RECURSIVE carries AS (
SELECT LitCircuit.LitCircuitID FROM LitCircuit
JOIN CircuitLayoutItem ON LitCircuit.CircuitLayoutID = CircuitLayoutItem.CircuitLayoutID
WHERE CircuitLayoutItem.TableName = 'LitCircuit' AND CircuitLayoutItem.TablePK IN (00340)
UNION ALL
SELECT LitCircuit.LitCircuitID FROM carries
JOIN CircuitLayoutItem ON carries.LitCircuitID = CircuitLayoutItem.TablePK
JOIN LitCircuit ON CircuitLayoutItem.CircuitLayoutID = LitCircuit.CircuitLayoutID
WHERE CircuitLayoutItem.TableName = 'LitCircuit'
)
SELECT DISTINCT LitCircuitID FROM carries;
I did not think of the CTE as a table to query against - rather just a result set, so I did not realize you have to SELECT from it - or in general treat it like a table.
In SQL Server, I can use IF conditional structure to execute some statements if a condition is true. According to this and this, there seem to be no such structure in SQLite.
I want to check if a table exist, if it does, do nothing, if not, do a lot of things including creating tables, inserting and deleting data from other tables and updating as well:
CASE WHEN ((SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'TraitsSwap') = 1) THEN
-- 50 lines of code, including CREATE, DROP, INSERT, DELETE and UPDATE statements, with random() in used
ELSE
-- Do nothing
END
Is there anyway I can achieve this? The code includes usage of random() and it requires consistent result (i.e, only random in the first time). I am sorry if this sounds unreasonable, but this is in context of game modding, so I cannot really change the backend code to run separated transaction code.
I think there may be an alternative if there is a function in SQLite that can execute a string/statement block and return a result. For that, I can transform the query into
SELECT CASE WHEN ((SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'TraitsSwap') = 1) THEN
ExecuteCode("Code; RETURN 1;")
ELSE
0
END
I tried
SELECT CASE WHEN ((SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'TraitsSwap') = 1) THEN
SELECT 1;
INSERT INTO Foo(Test) VALUES("");
SELECT "A";
ELSE
SELECT 1;
SELECT 2;
SELECT "A";
END
but it's unsuccessful, the error is
near "SELECT": syntax error: SELECT CASE WHEN ((SELECT COUNT(*) FROM
sqlite_master WHERE type = 'table' AND name = 'TraitsSwap') = 1) THEN
SELECT
Is there a way to manipulate the query plan generated in SQLite?
I 'l try to explain my problem:
I have 3 tables:
CREATE TABLE "index_term" (
"id" INT,
"term" VARCHAR(255) NOT NULL,
PRIMARY KEY("id"),
UNIQUE("term"));
CREATE TABLE "index_posting" (
"doc_id" INT NOT NULL,
"term_id" INT NOT NULL,
PRIMARY KEY("doc_id", "field_id", "term_id"),,
CONSTRAINT "index_posting_doc_id_fkey" FOREIGN KEY ("doc_id")
REFERENCES "document"("doc_id") ON DELETE CASCADE,
CONSTRAINT "index_posting_term_id_fkey" FOREIGN KEY ("term_id")
REFERENCES "index_term"("id") ON DELETE CASCADE);;
CREATE INDEX "index_posting_term_id_idx" ON "index_posting"("term_id");
CREATE TABLE "published_files" (
"doc_id" INTEGER NOT NULL,,
"uri_id" INTEGER,
"user_id" INTEGER NOT NULL,
"status" INTEGER NOT NULL,
"title" VARCHAR(1024),
PRIMARY KEY("uri_id"));
CREATE INDEX "published_files_doc_id_idx" ON "published_files"("doc_id");
about 600.000 entries in the index_term, about 4 Millions in the index_posting and 300.000 in the published_files table.
Now when i want to find the number of unique doc_ids in index_posting which reference some terms i use the following SQL.
select count(distinct index_posting.doc_id) from index_term, index_posting
where
index_posting.term_id = index_term.id and index_term.term like '%test%'
The result is displayed in reasonable time (0.3 secs). Asking Explain Query plan returns
0|0|0|SCAN TABLE index_term
0|1|1|SEARCH TABLE index_posting USING INDEX index_posting_term_id_idx (term_id=?)
When i want to filter the count in the way that it only includes doc_ids of index_posting if there exists a published_files entry:
select count(distinct index_posting.doc_id) from index_term, index_posting,
published_files where
index_posting.term_id = index_term.id and index_posting.doc_id = published_files.doc_id and index_term.term like '%test%'
The query takes almost 10 times as long. Asking Explain Query plan returns
0|0|1|SCAN TABLE index_posting
0|1|0|SEARCH TABLE index_term USING INDEX sqlite_autoindex_index_term_1 (id=?)
0|2|2|SEARCH TABLE published_files AS pf USING COVERING INDEX published_files_doc_id_idx (doc_id=?)
So as far as i understand SQLITE changed here its query plan doing a full table scan of index_posting and a lookup in index_term instead of the other way around.
As a workaround i did do a
analyze index_posting;
analyze index_term;
analyze published_files;
and now it seems correct,
0|0|0|SCAN TABLE index_term
0|1|1|SEARCH TABLE index_posting USING INDEX index_posting_term_id_idx (term_id=?)
0|2|2|SEARCH TABLE published_files USING COVERING INDEX published_files_doc_id_idx (doc_id=?)
but my question is - is there a way to force SQLITE to always use the correct query plan?
TIA
ANALYZE is not a workaround; it's supposed to be used.
You can use CROSS JOIN to enforce a certain order of the nested loops, or use INDEXED BY to force a certain index to be used.
However, you asked for "the correct query plan", which might not be same as the one enforced by these mechanisms.
My stored proc is defined as
create or replace procedure TEST(
name IN table1.col_name%type,
price IN table1.col_price%type
)
is
begin
update table1 t set t.name =name where t.price = price;
commit;
end TEST;
I am trying to execute it as
exec TEST(name => 'John', price => 1000);
However, it gives invalid SQL error. What am i missing here?
Your input parameter %type statements claim the column names are col_name and col_price. But that is not how you refer to them in your stored procedure (name and price).
Bad things can happen when you name variables after column names. AskTom recommends a limited convention of variable naming conventions:
local variables start with L_
parameters start with P_
global package variables start with G_
That link has a good general discussion on PL/SQL naming conventions. I personally just use V_ for most variables (aside from indexes and other obvious things), but that's just me.
Lastly, the col_ in the column names seem redundant; simply use name and price as column names.
So, that said, I think this does what you want:
create table table1 (
name varchar2(30),
price number
);
create or replace procedure TEST(
p_name IN table1.name%type,
p_price IN table1.price%type
)
is
begin
update table1
set name = p_name
where price = p_price;
commit;
end TEST;
/
insert into table1 values ('John', 500);
commit;
select * from table1;
exec TEST(p_name => 'Bob', p_price => 500);
select * from table1;
-- Clean up test artifacts
drop procedure test;
drop table table1;
Giving the output:
table TABLE1 created.
PROCEDURE TEST compiled
1 rows inserted.
committed.
NAME PRICE
------------------------------ ----------
John 500
anonymous block completed
NAME PRICE
------------------------------ ----------
Bob 500
procedure TEST dropped.
table TABLE1 dropped.
I really don't understand the variable prefixing approach. Oracle don't do it with their own API's, and it would be extraordinarily irritating if they did. It always seems like a workaround, rather than a fix.
For me the fix is to namespace the variables with the procedure name. It keeps the argument names "clean" and makes your code 100% proof against capture:
create or replace procedure TEST(
name IN table1.col_name%type,
price IN table1.col_price%type)
is
begin
update table1 t
set name = test.name
where t.price = price;
commit;
end TEST;
Lots more info on capture here.
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.