I have three tables
[USER] --Master user table
[KEYWORD] --Master keyword table
[USER_KEYWORD] --[USER]-[KEYWORD] mapping table
Below is the structure in my db
GO
--master user table
CREATE TABLE [USER]
(
[USERID] INT IDENTITY,
[NAME] VARCHAR(50)
)
GO
--master keyword table
CREATE TABLE [KEYWORD]
(
[KEYWORDID] INT IDENTITY,
[KEYWORD] VARCHAR(50)
)
GO
--x table user_keyword
CREATE TABLE [USER_KEYWORD]
(
[USERID] INT ,
[KEYWORDID] INT
)
GO
--Insert data in master user table
INSERT INTO [USER]
SELECT 'TESTUSER1'
UNION ALL
SELECT 'TESTUSER2'
UNION ALL
SELECT 'TESTUSER3'
UNION ALL
SELECT 'TESTUSER4'
GO
--Insert data in master keyword table
INSERT INTO [KEYWORD]
SELECT 'ASP'
UNION ALL
SELECT 'ASP.NET 3.5'
UNION ALL
SELECT 'C#'
UNION ALL
SELECT 'JAVA'
UNION ALL
SELECT 'ASP.NET'
UNION ALL
SELECT 'SQL'
UNION ALL
SELECT 'SQL SERVER'
UNION ALL
SELECT 'SQL SERVER 2005'
UNION ALL
SELECT 'SQL SERVER 2008'
GO
--Insert data in user keyword table
INSERT INTO [USER_KEYWORD]
SELECT 1,1
UNION ALL
SELECT 2,2
UNION ALL
SELECT 3,3
UNION ALL
SELECT 4,4
UNION ALL
SELECT 1,2
UNION ALL
SELECT 2,3
UNION ALL
SELECT 3,4
UNION ALL
SELECT 4,1
UNION ALL
SELECT 2,3
UNION ALL
SELECT 3,4
UNION ALL
SELECT 4,6
UNION ALL
SELECT 3,6
UNION ALL
SELECT 4,6
UNION ALL
SELECT 2,7
UNION ALL
SELECT 3,8
UNION ALL
SELECT 4,9
GO
CREATE PROC TEST_SEARCH_KEYWORDS
#SEARCHKEYWORD VARCHAR(50)
AS
BEGIN
SELECT K.[KEYWORD],COUNT(UK.[KEYWORDID]) AS [KEWWORDCOUNT] FROM [KEYWORD] K
INNER JOIN [USER_KEYWORD] UK
ON K.[KEYWORDID]=UK.[KEYWORDID]
WHERE K.[KEYWORD] LIKE (#SEARCHKEYWORD+ '%')
GROUP BY K.[KEYWORD]
END
--TEST EXAMPLES
EXEC TEST_SEARCH_KEYWORDS 'ASP'
--0UTPUT
KEYWORD KEWWORDCOUNT
-------------------------------------------------- ------------
ASP 2
ASP.NET 3.5 2
--TEST EXAMPLES
EXEC TEST_SEARCH_KEYWORDS 'SQL'
--0UTPUT
KEYWORD KEWWORDCOUNT
-------------------------------------------------- ------------
SQL 3
SQL SERVER 1
SQL SERVER 2005 1
SQL SERVER 2008 1
I have one sp named TEST_SEARCH_KEYWORDS to search records based on keywords that is provided from outside.Currently i am using like condition in sp. I want to know that is this will be good to search records from database using like as in future my records will keep on increasing .I don't want to use full text search
as i have to cope with sql server 2000 also.
if you keep searching like:
WHERE K.[KEYWORD] LIKE (#SEARCHKEYWORD+ '%')
then your query will use an index, and should still perform well as the table grows. However, if you change it to:
WHERE K.[KEYWORD] LIKE ('%'+ #SEARCHKEYWORD+ '%')
then it will not, and it will table scan, which will cause bad performance as your table grows.
One (storage-hungry) solution would be to pre-compute and store a number keyword prefices alongside the actual keyword itself. Then based on the look criteria you would do a direct equality comparison on the relevant prefix column (if one existed) or else fall back to performing a "like" comparison.
For example, suppose to impose a limit that the search word must contain at least 3 characters. You could create columns Prefix3Chars, Prefix4Chars, Prefix5Chars. Say the user enters the search word "Hello" and your database contained a key word "Hello, World". You would elect to search on the Prefix5Chars column and would match against the key word "Hello, World". If the user performed a search with key word "Hello, W" you would fall back to searching using "like".
Related
How can I use a dynamic query as an input source for a larger query?
There is a query I'm getting the union of values in different datasets/tables scattered around and the list is growing so I'm thinking of using the dynamic query than to write queries for each tables like this
SET QUERY = "";
SET tables = ["table1", "table2"...];
SET tables_size = ARRAY_LENGTH(tables);
WHILE i < tables_size DO
IF (i = tables_size -1) THEN
BEGIN
SET query = CONCAT(query, " SELECT id, name FROM ", tables[OFFSET(i)]);
BREAK;
END;
ELSE
SET query = CONCAT(query, " SELECT id, name FROM ", tables[OFFSET(i)], ' UNION ALL ');
END IF;
SET i = i + 1;
END WHILE;
EXECUTE IMMEDIATE query;
My goal is to use the output of the executed query as a FROM clause for a larger query.
It will be something like
Select A, B, C, D ... From *EXECUTE IMMEDIATE query* LEFT JOIN ... ON..
Is there a way to inject an output of a dynamic query as a table for another query?
I don't see TABLE as a variable type for bigquery so that was not my option.
I'm getting a bit tired of copy pasting table names to the exact query every time a new table is introduced to this logic.
SELECT id, name FROM table1 UNION ALL
SELECT id, name FROM table1 UNION ALL
SELECT id, name FROM table3...
If there is a simple way to do this? or maybe a reason to not use dynamic queries for performance reasons?
Hope one of these are helpful:
1. Wildcard tables
If tables you want to union have a common prefix, you can consider to use a wildcard table like below. I think this is more concise form rather than union-all:
-- Sample Tables
CREATE TABLE IF NOT EXISTS testset.table1 AS SELECT 1 AS id, 'aaa' AS name;
CREATE TABLE IF NOT EXISTS testset.table2 AS SELECT 2 AS id, 'bbb' AS name;
CREATE TABLE IF NOT EXISTS testset.table3 AS SELECT 3 AS id, 'ccc' AS name;
--- Wildcard tables
SELECT * FROM `testset.table*` WHERE _TABLE_SUFFIX IN ('1', '2', '3');
2. Dynamic SQL & Temp Table
You can't inject a dynamic SQL directly into another query but you can use a temp table to emulate it.
2.1 Dynamic SQL
More concise dynamic query to union all tables:
DECLARE tables DEFAULT ["testset.table1", "testset.table2", "testset.table3"];
SELECT ARRAY_TO_STRING(ARRAY_AGG(FORMAT('SELECT id, name FROM %s', t)), ' UNION ALL\n')
FROM UNNEST(tables) t;
2.2 Using a temp table
I thinks you can modify your larger query to use a dynamically generated temp table.
DECLARE tables DEFAULT ["testset.table1", "testset.table2", "testset.table3"];
CREATE TABLE IF NOT EXISTS testset.table1 AS SELECT 1 AS id, 'aaa' AS name;
CREATE TABLE IF NOT EXISTS testset.table2 AS SELECT 2 AS id, 'bbb' AS name;
CREATE TABLE IF NOT EXISTS testset.table3 AS SELECT 3 AS id, 'ccc' AS name;
EXECUTE IMMEDIATE (
SELECT "CREATE TEMP TABLE IF NOT EXISTS union_tables AS \n"
|| ARRAY_TO_STRING(ARRAY_AGG(FORMAT('SELECT id, name FROM %s', t)), ' UNION ALL\n') FROM UNNEST(tables) t
);
-- your larger query using a temp table
SELECT * FROM union_tables;
output:
This might be a weird question (and maybe not possible), but how do I create a select statement in oracle to select customs strings ?
For example, we can use the following:
SELECT 1, 2, 3, 4, 5
FROM DUAL
and it will select a table with 5 columns, with values 1 to 5 respectively. I tried selecting strings, but it won't work, since dual has a column of type VARCHAR2(1)...
I want to create a table with column names (for example, apples, bananas, oranges) and then pivot it to have one column column_name ad every row representing one of my fruits and then iterate to do my operations.
Note that I do not want to create a table nor a view, this data will only be used in this query, and it's not available elsewhere.
Is this what you want?
select 'apples' as colname from dual union all
select 'bananas' as colname from dual union all
select 'oranges' as colname from dual ;
You can put this logic into a CTE or subquery.
So I can't create or edit tables (I'm a user with read only permission) and I want to look up 10,000 unique id's. I can't put them inside of an IN() statement because oracle limits over 1000 items.
Is it possible to select this entire list from the DUAL table in oracle? Something like:
select
'id123,id8923,id32983,id032098,id308230,id32983289'
from DUAL
Use a collection (they are not limited to 1000 items like an IN clause is):
SELECT COLUMN_VALUE AS id
FROM TABLE(
SYS.ODCIVARCHAR2LIST(
'id123', 'id8923', 'id32983', 'id032098', 'id308230', 'id32983289'
)
)
SYS.ODCIVARCHAR2LIST and SYS.ODCINUMBERLIST are collection types that are supplied in the SYS schema.
You can join this directly to whichever table you are SELECTing from without needing to use the DUAL table:
SELECT y.*
FROM your_table y
INNER JOIN TABLE(
SYS.ODCIVARCHAR2LIST(
'id123', 'id8923', 'id32983', 'id032098', 'id308230', 'id32983289'
)
) i
ON (y.id = i.COLUMN_VALUE);
If you can get a collection type created then you do not even need the TABLE expression and can use it directly in the WHERE clause using the MEMBER OF operator:
CREATE OR REPLACE TYPE stringlist IS TABLE OF VARCHAR2(200);
/
SELECT *
FROM yourtable
WHERE id MEMBER OF stringlist(
'id123', 'id8923', 'id32983', 'id032098', 'id308230', 'id32983289'
);
You can even pass the values as a bind parameter - see my answer here
Oracle still doesn't support the VALUES row constructor, so there are only two ugly workarounds:
The 1000 item limit does not apply for multi-column IN conditions
Expression Lists
A comma-delimited list of expressions can contain no more than 1000
expressions. A comma-delimited list of sets of expressions can contain
any number of sets, but each set can contain no more than 1000
expressions.
so you can do:
where (1,id) in ( (1,'id123'),
(1,'id8923'),
(1,'id32983'),
(1,'id032098'), .... )
Or using a big ugly UNION ALL:
with idlist (xid) as (
select 'id123' from dual union all
select 'id8923' from dual union all
.....
select 'id32983' from dual
)
select ...
from some_table
where id in (select xid from idlist);
One solution is the WITH clause:
with ids as (
select 'id123' as uid from dual union all
select 'id8923' as uid from dual union all
select 'id32983' as uid from dual union all
select 'id032098' as uid from dual union all
select 'id308230' as uid from dual union all
select 'id32983289' as uid from dual
)
select *
from ids
join your_table yt
on yt.id = ids.uid
This may seem like a bit of a chore but presumably you have your list of UIDs in a spreadsheet or whatever. If so it's a cinch to generate those select statements using regular expressions. Just cut'n'paste the column into an editor which supports regex search and replace.
Yet another work-around
select *
from t
where id in ('id1','id2','id3',...,'id1000')
or id in ('id1001','id1002','id1003',...,'id2000')
or id in ('id2001','id2002','id2003',...,'id3000')
or ...
I want a SQL statement which can run on all the platforms and give the result in tree structure (not a real tree structure though)format.i.e all the related columns appears together. is it possible to achieve the following format using a sql. I have a simple table with three columns(GROUP_STEP,PREDECESSOR,COLUMNNUM). expected output
Platform supported : Oracle, SQL Server, DB2 and Sybase. I am looking for a SELECT statement from the table having following data in different format.
After #diaho suggestion , following is the output
select groupnum,predecessor ,count(groupnum) as columnum group by groupnum,predecessor
if you give more details on table schema answer may change !!!
Based on your table, I'm assuming that you only have the Group_Step and Predecessor columns in your raw table and that the ColumnNum column represents the total number of levels for a leaf in your tree. For example, since PC_Wrap1 has the Predecessor BENEFITS which has the Predecessor COPY_BUDG it has a total of 3 'levels'. If that is the case, you need a recursive query to calculate the ColumnNum value. I can't speak to all platforms, but for SQL Server, you can use a CTE:
EDIT: Removed 'dreaded non-standard square brackets' per a_horse_with_no_name's suggestion :)
-- Setup table
CREATE TABLE #Temp
(
Group_Step VARCHAR(100),
Predecessor VARCHAR(100)
)
-- Setup dummy data
INSERT INTO #Temp
(
Group_Step,
Predecessor
)
SELECT 'ACT_BD_ACT', '' UNION
SELECT 'COPY_BUDG', '' UNION
SELECT 'COPY_BUDG2', '' UNION
SELECT 'BENEFITS', 'COPY_BUDG' UNION
SELECT 'BENEFITS', 'COPY_BUDG2' UNION
SELECT 'PC_WRAP1', 'BENEFITS' UNION
SELECT 'PC_WRAP2', 'BENEFITS' UNION
SELECT 'ALLC1', '' UNION
SELECT 'ALLC2', '' UNION
SELECT 'ALLC3', 'ALLC2' UNION
SELECT 'TCP1', 'ALLC3' UNION
SELECT 'TCP1', 'ALLC4' UNION
SELECT 'COPY_BUDG3', '' UNION
SELECT 'COPY_BUDG4', '';
-- Actual solution starts here:
WITH Result
(
Group_Step,
Predecessor,
ColumnNum
)
AS
(
-- Anchor member definition
SELECT
Group_Step,
Predecessor AS Predecessor,
1 AS ColumnNum
FROM
#Temp
WHERE
Predecessor = ''
UNION ALL
-- Recursive member definition
SELECT
t.Group_Step,
t.Predecessor,
ColumnNum + 1 AS ColumnNum
FROM
#Temp AS t
JOIN
Result AS r
ON
t.Predecessor = r.Group_Step
)
-- Statement that executes the CTE
SELECT DISTINCT
Group_Step,
Predecessor,
ColumnNum
FROM
Result
-- EDIT #2: Adding ORDER BY per Op's comment
ORDER BY
ColumnNum
DROP TABLE #Temp
How can I find a missing values from a set of values, using SQL (Oracle DB)
e.g.
SELECT NAME
FROM ACCOUNT
WHERE ACCOUNT.NAME IN ('FORD','HYUNDAI','TOYOTA','BMW'...)
(The "IN" clause may contain hundreds of values)
If 'HYUNDAI' is missing in the ACCOUNT table, I need to get the result as "HYUNDAI".
Currently I use the result of the above query to do a Vlookup against the original set of values to find the missing values, I want to directly get the missing values without doing the Vlookup.
Thanks
Kiran,
You got it reversed. Do this: http://www.sqlfiddle.com/#!2/09239/3
SELECT Brand
FROM
(
-- Oracle can't make a row without a table, need to use DUAL dummy table
select 'FORD' as Brand from dual union
select 'HYUNDAI' from dual union
select 'TOYOTA' fom dual union
select 'BMW' from dual
) x
where Brand not in (select BrandName from account)
Sample Account data:
create table account(AccountId int, BrandName varchar(10));
insert into account(AccountId, BrandName) values
(1,'FORD'),
(2,'TOYOTA'),
(3,'BMW');
Output:
| BRAND |
-----------
| HYUNDAI |
Better yet, materialized the brands to a table:
select *
from Brand
where BrandName not in (select BrandName from account)
Output:
| BRANDNAME |
-------------
| HYUNDAI |
Sample data and live test: http://www.sqlfiddle.com/#!2/09239/1
CREATE TABLE Brand
(`BrandName` varchar(7));
INSERT INTO Brand
(`BrandName`)
VALUES
('FORD'),
('HYUNDAI'),
('TOYOTA'),
('BMW');
create table account(AccountId int, BrandName varchar(10));
insert into account(AccountId, BrandName) values
(1,'FORD'),
(2,'TOYOTA'),
(3,'BMW');
You should use Except: EXCEPT returns any distinct values from the left query that are not also found on the right query.
WITH SomeRows(datacol) --It will look for missing stuff here
AS( SELECT *
FROM ( VALUES ('FORD'),
('TOYOTA'),
('BMW')
) AS F (datacol)),
AllRows (datacol) --This has everthing
AS( SELECT *
FROM ( VALUES ('FORD'),
('HYUNDAI'),
('TOYOTA'),
('BMW')
) AS F (datacol))
SELECT datacol
FROM AllRows
EXCEPT
SELECT datacol
FROM SomeRows
You can do:
SELECT a.val
FROM
(
SELECT 'FORD' val UNION ALL
SELECT 'HYUNDAI' UNION ALL
SELECT 'TOYOTA' UNION ALL
SELECT 'BMW' UNION ALL
etc...
etc...
) a
LEFT JOIN account b ON a.val = b.name
WHERE b.name IS NULL
This worked perfectly, thanks Michael.
SELECT Brand
FROM
( -- Oracle can't make a row without a table, need to use DUAL dummy table
select 'FORD' as Brand from dual union
select 'HYUNDAI' from dual union
select 'TOYOTA' fom dual union
select 'BMW' from dual
)
where Brand not in (select BrandName from account)
Luxspes and Zane thank you for your inputs
Contributing Excel code to make the typing of the answer easier:
Say column A has the values (Ford, Hyundai,...).
In column B, put this in every cell:
select 'x' as brand from dual union
In column C, write this formula, and copy it down.
=REPLACE(A2,9,1,A1)
All of the select/union statements should appear in column C.