So I came across a problem/question yesterday.
I am building a chat (with AJAX) and use two tables:
TABLE users -> 'name', 'username', 'password', 'time'
TABLE messages -> 'sendFrom', 'sendTo', 'message', 'time'
So an example message now would be
'foo' | 'bar' | 'Hey, how are you?' | 130611134427611
I was told the correct way to do this is, instead, to use an ID column, and use that as a Primary Key instead of the username (which, anyway, makes sense).
OK, so now this looks like
TABLE users -> 'ID', 'name', 'username', 'password', 'time'
TABLE messages -> 'sendFrom', 'sendTo', 'message', 'time'
So an example message now would be
'22' | '7' | 'Hey, how are you?' | 130611134427611
I've managed to JOIN both tables to return the rows as on the first example message, but since I am detecting user keypresses too, I need to scan the table twice, so:
SELECT *
FROM (SELECT *
FROM (SELECT *
FROM messages
WHERE sendTo = '$username'
AND time > (SELECT time FROM users
WHERE username = '$username' LIMIT 1)
AND message <> '$keypressCode'
ORDER BY time DESC LIMIT 30)
ORDER BY time ASC)
UNION
SELECT *
FROM (SELECT *
FROM messages
WHERE message = '$keypressCode'
AND time > (SELECT time FROM users
WHERE username = '$username' LIMIT 1)
AND sendTo = '$username' LIMIT 1);
But now, of course, I don't just select from messages; instead, I use a long query like
SELECT * FROM (
SELECT u1.ID as sendTo, u2.ID as sendFrom, messages.message, .....
.....
.....
.....
.....
) as messages;
that MUST BE INSERTED just in the place of messages (I haven't tried this yet, but I think is like that. See, the thing is I DuckDuckGo'ed and Googled and found nothing, so I came here)
My first question is:
Is there a way to use ALIAS for the table messages so I don't have to scan it TWICE? So, instead, I just save the above query using ALIAS as a table called messages and select data from it twice, once in each part of UNION.
In addition, the answer to the first question would also be an answer for:
Is there a way to use ALIAS to save the time selected from the table? (since, again, I am searching for it TWICE).
In practice, what I am doing may not be unefficient (since there will be at most 20 users), but what if?
Also, I am a mathematician, and like it or not, I like to worry a lot about efficiency!
Thank you so much in advance, I hope I made myself clear.
I am not sure but it does look as if you want a view.
Define that query like this:
CREATE VIEW MyMessageView
AS
SELECT ...
FROM ...
...
Now you can use that view in any context where an ordinary table can be used: in a FROM clause, in a JOIN clause, as a subquery etc.:
SELECT ...
FROM MyMessageView
WHERE ...
...
UNION
SELECT ...
FROM MyMessageView
WHERE ...
Instead of using UNION, put an OR in the condition:
SELECT *
FROM messages
WHERE time > (SELECT time FROM users
WHERE username = '$username' LIMIT 1)
AND (message <> '$keypressCode' AND sendTo = '$username'
OR message = '$keypressCode')
I am answering my own question, since I consider what people might be looking for is a VIEW.
First, define that query like this:
CREATE VIEW MyViewTable
AS
SELECT ...
FROM ...
...
...;
Now you can use that view (which is a sepparate query) in any context where an ordinary table can be used: in a FROM clause, in a JOIN clause, as a subquery etc.:
SELECT ...
FROM MyViewTable
WHERE ...
...
UNION
SELECT ...
FROM MyViewTable
WHERE ...
but with a few restrictions:
You cannot SELECT from your view using subqueries, such as
SELECT * FROM MyViewTable WHERE someColumn = (SELECT ... ...)
but (as normal) you can use subqueries when creating the VIEW and in the main query.
The SELECT statement cannot refer to prepared statement parameters.
The definition cannot refer to a TEMPORARY table, and you cannot create a TEMPORARY view.
(there are more, but this are, in my opinion, among the most common queries, so the restrictions might be among the most common errors. See SQLite reference for more information. ).
Related
There is another question with this title - sorry to duplicate, but I simply cannot understand the answers to that question, so I assume plenty of others won't either and it will be useful to have a clear answer.
I am working in Postgres. I have a stored procedure called duplicate_project that takes a single parameter, the project ID.
I'd like to add a dynamic parameter in a WHERE clause in a SELECT query, to avoid having to hard-code a number.
So I'd like to avoid hardcoding:
SELECT duplicate_project(578);
And instead have something like this:
SELECT duplicate_project(SELECT project_id FROM folder WHERE name='foo' LIMIT 1);
Obviously this won't work, but is there a straightforward way to do something like it?
Do you mean something like this:
select duplicate_project(f.project_id)
from folder f
where name = 'foo'
limit 1;
The limit is not really needed. In that case you get multiple rows that would show the result of duplicate_project() for each project_id returned.
Or:
SELECT duplicate_project( (SELECT project_id FROM folder WHERE name='foo' LIMIT 1) );
One method would be a scalar subquery:
SELECT duplicate_project( (SELECT project_id FROM folder WHERE name='foo' LIMIT 1) );
Another would be to use a FROM clause:
SELECT duplicate_project(x.project_id)
FROM (SELECT project_id FROM folder WHERE name = 'foo' LIMIT 1) x;
I have the following table:
id symbol_01 symbol_02
1 abc xyz
2 kjh okd
3 que qid
I need a query that ensures symbol_01 and symbol_02 are both contained in a list of valid symbols. In other words I would needs something like this:
select *
from mytable
where symbol_01 in (
select valid_symbols
from somewhere)
and symbol_02 in (
select valid_symbols
from somewhere)
The above example would work correctly, but the subquery used to determine the list of valid symbols is identical both times and is quite large. It would be very innefficient to run it twice like in the example.
Is there a way to do this without duplicating two identical sub queries?
Another approach:
select *
from mytable t1
where 2 = (select count(distinct symbol)
from valid_symbols vs
where vs.symbol in (t1.symbol_01, t1.symbol_02));
This assumes that the valid symbols are stored in a table valid_symbols that has a column named symbol. The query would also benefit from an index on valid_symbols.symbol
You could try use a CTE like;
WITH ValidSymbols AS (
SELECT DISTINCT valid_symbol
FROM somewhere
)
SELECT mt.*
FROM MyTable mt
INNER JOIN ValidSymbols v1
ON mt.symbol_01 = v1.valid_symbol
INNER JOIN ValidSymbols v2
ON mt.symbol_02 = v2.valid_symbol
From a performance perspective, your query is the right way to do this. I would write it as:
select *
from mytable t
where exists (select 1
from valid_symbols vs
where t.symbol_01 = vs.valid_symbol
) and
exists (select 1
from valid_symbols vs
where t.symbol_02 = vs.valid_symbol
) ;
The important component is that you need an index on valid_symbols(valid_symbol). With this index, the lookup should be pretty fast. Appropriate indexes can even work if valid_symbols is a view, although the effect depends on the complexity of the view.
You seem to have a situation where you have two foreign key relationships. If you explicitly declare these relationships, then the database will enforce that the columns in your table match the valid symbols.
I have a component that retrieves data from database based on the keys provided.
However I want my java application to get all the data for all keys in a single database hit to fasten up things.
I can use 'in' clause when I have only one key.
While working on more than one key I can use below query in oracle
SELECT * FROM <table_name>
where (value_type,CODE1) IN (('I','COMM'),('I','CORE'));
which is similar to writing
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'COMM'
and
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'CORE'
together
However, this concept of using 'in' clause as above is giving below error in 'SQL server'
ERROR:An expression of non-boolean type specified in a context where a condition is expected, near ','.
Please let know if their is any way to achieve the same in SQL server.
This syntax doesn't exist in SQL Server. Use a combination of And and Or.
SELECT *
FROM <table_name>
WHERE
(value_type = 1 and CODE1 = 'COMM')
OR (value_type = 1 and CODE1 = 'CORE')
(In this case, you could make it shorter, because value_type is compared to the same value in both combinations. I just wanted to show the pattern that works like IN in oracle with multiple fields.)
When using IN with a subquery, you need to rephrase it like this:
Oracle:
SELECT *
FROM foo
WHERE
(value_type, CODE1) IN (
SELECT type, code
FROM bar
WHERE <some conditions>)
SQL Server:
SELECT *
FROM foo
WHERE
EXISTS (
SELECT *
FROM bar
WHERE <some conditions>
AND foo.type_code = bar.type
AND foo.CODE1 = bar.code)
There are other ways to do it, depending on the case, like inner joins and the like.
If you have under 1000 tuples you want to check against and you're using SQL Server 2008+, you can use a table values constructor, and perform a join against it. You can only specify up to 1000 rows in a table values constructor, hence the 1000 tuple limitation. Here's how it would look in your situation:
SELECT <table_name>.* FROM <table_name>
JOIN ( VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b) ON a = value_type AND b = CODE1;
This is only a good idea if your list of values is going to be unique, otherwise you'll get duplicate values. I'm not sure how the performance of this compares to using many ANDs and ORs, but the SQL query is at least much cleaner to look at, in my opinion.
You can also write this to use EXIST instead of JOIN. That may have different performance characteristics and it will avoid the problem of producing duplicate results if your values aren't unique. It may be worth trying both EXIST and JOIN on your use case to see what's a better fit. Here's how EXIST would look,
SELECT * FROM <table_name>
WHERE EXISTS (
SELECT 1
FROM (
VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b)
WHERE a = value_type AND b = CODE1
);
In conclusion, I think the best choice is to create a temporary table and query against that. But sometimes that's not possible, e.g. your user lacks the permission to create temporary tables, and then using a table values constructor may be your best choice. Use EXIST or JOIN, depending on which gives you better performance on your database.
Normally you can not do it, but can use the following technique.
SELECT * FROM <table_name>
where (value_type+'/'+CODE1) IN (('I'+'/'+'COMM'),('I'+'/'+'CORE'));
A better solution is to avoid hardcoding your values and put then in a temporary or persistent table:
CREATE TABLE #t (ValueType VARCHAR(16), Code VARCHAR(16))
INSERT INTO #t VALUES ('I','COMM'),('I','CORE')
SELECT DT. *
FROM <table_name> DT
JOIN #t T ON T.ValueType = DT.ValueType AND T.Code = DT.Code
Thus, you avoid storing data in your code (persistent table version) and allow to easily modify the filters (without changing the code).
I think you can try this, combine and and or at the same time.
SELECT
*
FROM
<table_name>
WHERE
value_type = 1
AND (CODE1 = 'COMM' OR CODE1 = 'CORE')
What you can do is 'join' the columns as a string, and pass your values also combined as strings.
where (cast(column1 as text) ||','|| cast(column2 as text)) in (?1)
The other way is to do multiple ands and ors.
I had a similar problem in MS SQL, but a little different. Maybe it will help somebody in futere, in my case i found this solution (not full code, just example):
SELECT Table1.Campaign
,Table1.Coupon
FROM [CRM].[dbo].[Coupons] AS Table1
INNER JOIN [CRM].[dbo].[Coupons] AS Table2 ON Table1.Campaign = Table2.Campaign AND Table1.Coupon = Table2.Coupon
WHERE Table1.Coupon IN ('0000000001', '0000000002') AND Table2.Campaign IN ('XXX000000001', 'XYX000000001')
Of cource on Coupon and Campaign in table i have index for fast search.
Compute it in MS Sql
SELECT * FROM <table_name>
where value_type + '|' + CODE1 IN ('I|COMM', 'I|CORE');
Postgres 9.4
I guess, queries like this is not the best approach in terms of database performance:
SELECT t.name,
t.description,
t.rating,
t.readme,
t.id AS userid,
t.notifications
FROM ( SELECT "user".name,
"user".description,
"user".rating,
"user".readme,
"user".id,
( SELECT array_to_json(array_agg(row_to_json(notifications.*))) AS array_to_json
FROM ( SELECT notification.id,
notification.action_type,
notification.user_id,
notification.user_name,
notification.resource_id,
notification.resource_name,
notification.resource_type,
notification.rating,
notification.owner
FROM notification
WHERE (notification.owner = "user".id)
ORDER BY notification.created DESC) notifications) AS notifications
FROM "user") t
Column notification contains json object with all the matched rows from notification table.
How should I rebuild this query to receive data in the same manner? I suppose, I should use JOIN commands somehow.
I have request, which utilise more than one inherited SELECT.
Thank you for your time!
The outermost query only aliases id to userid. You can move the alias to the inner query, and omit the outer query entirely.
Then you can create a function to create the notification JSON:
create or replace function get_user_notifications(user_id bigint)
returns json language sql as
$$
select array_to_json(array_agg(row_to_json(n)))
from (
select id
, action_type
, ... other columns from notification ...
from notification
-- Use function name to refer to parameter not column
where user_id = get_user_notifications.user_id
order by
created desc
) n
$$;
Now you can write the query as:
select id as userid
, ... other columns from "user" ...
, get_user_notifications(id) as notifications
from "user" u;
Which looks a lot better, at the cost of having to maintain Postgres functions.
i get 2 names as the output of the first query....
eg: paul,peter
now this should be the input for the second query,
which has to display paul's and peter's email ids....
For nested queries I would strongly recommend WITH clause. It makes long complex queries order of magnitude easier to understand / construct / modify:
WITH
w_users AS( -- you can name it whatever you want
SELECT id
FROM users
WHERE < long condition here >
),
w_other_subquery AS(
...
)
SELECT email_id
FROM ...
WHERE user_id IN (SELECT id FROM w_users)
You can use like this
LIKE
SELECT USER_ID,EMAIL_ID FROM USERS where user_id IN
(SELECT PRODUCT_MEMBERS FROM PRODUCT WHERE PRODUCT_NAME='ICP/RAA');
Just use the IN clause '=' is used for matching one result
You can use In Command to get result
ex:
SELECT email FROM tableName WHERE (Name IN ('paul', 'peter'))