Changing the RoR SQL call to NAME instead of * - sql

Being that it is better to call a SQL database with the specific item, for example:
Bad:
SELECT * FROM tblUsers
Good:
SELECT email FROM tblUsers
How would I do this in RoR to make it explicitly perform the latter example?

Assuming your tblUsers table is used by User model, you should do
User.all(:select => "email")

Related

PostgreSQL subqueries as values

I am trying to use a postgreSQL INSERT query with a subquery as parameter value. This is to find the corresponding user_id from an accompanying auth_token in user_info tabel first and then create a new entry in a different table with the corresponding user_id.
My query looks something like this
INSERT INTO user_movies(user_id, date, time, movie, rating)
VALUES ((SELECT user_id FROM user_info where auth_token = $1),$2,$3,$4,$5)
RETURNING *
I know that a query such as this will work with a single value
INSERT INTO user_movies(user_id)
SELECT user_id FROM user_info where auth_token = $1
RETURNING *
but how do I allow for multiples input values. Is this even possible in postgreSQL.
I am also using nodejs to run this query -> therefore the $ as placeholders.
To expand on my comment (it is probably a solution, IIUC): Easiest in this case would be to make the inner query return all the values. So, assuming columns from the inner query have the right names, you could just
INSERT INTO user_movies(user_id, date, time, movie, rating)
SELECT user_id,$2,$3,$4,$5 FROM user_info where auth_token = $1
RETURNING *
Note this form is also without VALUES, it uses a query instead.
Edited 20220424: a_horse_with_no_name removed the useless brackets around SELECT ... that appeared in my original version; thanks!
YOu could try uising where IN clause
INSERT INTO user_movies(user_id)
SELECT user_id
FROM user_info
WHERE auth_token IN ($1,$2,$3,$4,$5)
RETURNING *

Sequelize query with NOT IN clause and nested SELECT

I have an SQL query which I would like to express using the sequelize query syntax.
DB Scheme
Users: (id, username, ...)
Groups: (id, name, ...)
User_Groups: (id, #groupId, #userId)
Query Description
Retrieve all users that are not yet part of a particular group.
Raw SQL Query (working)
SELECT User.id, User.email FROM Users as User
WHERE User.id NOT IN (
SELECT User_Group.userId FROM User_Groups as User_Group
WHERE User_Group.groupId = ${groupId}
)
Where I'm blocked
I tried to simulate a join using the sequelize include statement, but my brain is stuck transforming the nested SELECT query as well as the NOT IN operator...
It's not possible to represent sub-queries other than using Sequelize.literal in the where clause like this:
const users = await database.User.findAll({
where: Sequelize.where(Sequelize.literal(`(User.id NOT IN (
SELECT User_Group.userId FROM User_Groups as User_Group
WHERE User_Group.groupId = '${groupId}'
))`)
}
or you can mix up Op.notIn with Sequelize.literal

SQL Injection Method

The Injection Procedures are :
SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1;
But, My Question Is how the injection query is working in the sql?
its when you have your query as string in your code, something like this
Query = "SELECT UserId, Name, Password FROM Users WHERE UserId = '" + sUserID + "'"
So you pass sUserID = "ABC' OR 1=1;"
this will be translated like
SELECT UserId, Name, Password FROM Users WHERE UserId = 'ABC' OR 1=1
Since the condition 1=1 is always true, adding it at the end of a WHERE statement renders it irrelevant, and always true, as if the WHERE statement does not exist at all. Thus, the query is always executed, regardless of any other conditions added to the WHERE statement.
In the example you provided, If you allow your users to write down their own userID, they can write 105 or 1=1 in the input fields or in a website's URL address, and since or 1=1 makes UserId=105 useless, and the query will always select the data, hence the SQL injection.

Use same alias for union query SQL

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. ).

Inline table in Oracle SQL

I'm trying to integrate with some software (that I can't modify) that queries a database that I can modify.
I can give this software SQL queries, like so "select username, firstname, lastname from users where username in ?"
The software than fills in the ? with something like ('alice', 'bob'), and gets user information for them.
Thing is, there's another piece of software, which I again can't modify, which occasionally generates users like 'user2343290' and feeds them through to the first piece of software. Of course, it throws errors because it can't find that user.
So the query I want to run is something like this:
select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from create_table(?) t
where create_table generates a table with the rows mentioned in ?, with the first column named column1.
Or alternatively:
select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from _universe_ t where t.column1 in ?
where _universe_ is some fake table that contains possible every value in column1 (i.e. infinitely large).
I've tried select ? from dual, but unfortunately this only worked when ? was something like ('x'), not ('x', 'y').
Keep in mind I can't change the format of how the ? comes out, so I can't do select 'alice' from dual union all select 'bob' from dual.
Anyone know how I could do what I've mentioned, or something else to have a similar effect?
You can turn the delimited string of names into a table type like so:
CREATE TYPE name_tab AS TABLE OF VARCHAR2(30);
/
SELECT * FROM table(name_tab('alice','bob'));
So you would just need to create the type then your example would become:
select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from table(name_tab ?) t
(I'm assuming that the ? is replaced by simple text substitution -- because the IN wouldn't work if it was done as a bind variable -- and that the substituted text includes the parentheses.)
However, I am not sure the result of this will be helpful, since when a list of good usernames is given, you'll now have two result rows for each username, one with the actual information and another with the 'Unknown' values.
A better way to phrase the query might be:
select t.column_value username,
NVL(users.firstname,'Unknown'),
NVL(users.lastname,'Unknown')
from table(name_tab ?) t left join users on users.username = t.column_value
That should give you one row per username, with the actual data if it exists, or the 'Unknown' values if it does not.
You could use a pipelined function:
create type empname_t is table of varchar2(100);
create or replace function to_list(p_Names in string) return empname_t pipelined is
begin
pipe row(p_Names);
return;
end;
select * from table(to_list('bob'))
If you need to split the names (e.g. 'bob,alice'), you could use a function accepting a string and returning a empname_t, e.g. Tom Kyte's in_list, see
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:210612357425
and modify the to_list function to iterate over the collection and pipe each item from the collection.