delete a line beginning with N characters SQL Postgres - sql

I have 2 tables SQL:
USER with columns ->: uid, name, mail
Phone ->: id, brand, model, refFirtUser
refFirtUser is a column that constitutes the user's id - and a random number
example : refFirtUser (uid_users-12345687) -> 9145-12345687
i want to delete in table "Phone" all data .
I must to get "uid" of user by mail and to search N first characters before "-".
all data in column "refFirtUser " starting with "uid-xxxxx"
(where uid is the id of the user).
the only data I have is email
How should I do?

If you want to delete the from phone the user with mail 'someone#gmail.com':
delete from phone
where
refFirtUser like (select uid from user where mail = 'someone#gmail.com') || '-%'

It is ambiguous from your explanation whether user.uid is the number before - or after. In either case, you may use exists
delete from phone p
where exists ( select 1 from user u where mail in ( 'listofmail')
and p.refFirtUser like '%-'||u.id );
OR
delete from phone p
where exists ( select 1 from user u where mail in ( 'listofmail')
and p.refFirtUser like u.id||'-%' );

Related

How to verify table update and migrate data from another table - postgresql

I have following two tables in my potgres database with each type.
user
userid | bigint (PK) NOT NULL
username | character varying(255)
businessname | character varying(255)
inbox
messageid | bigint (PK) NOT NULL
username | character varying(255)
businessname | character varying(255)
What i wanna achieve here is i want to add a new field called userRefId to inbox table and migrate data on user table's userid data into that where each username and businessname match in both tables.
These are the queries i use to do that.
ALTER TABLE inbox ADD userRefId bigint;
UPDATE inbox
SET userRefId = u.userid
from "user" u
WHERE u.username = inbox.username
AND u.businessname = inbox.businessname;
Now i want to verify the data has been migrated correctly. what are the approaches i can take to achieve this? (Note : the username on inbox can be null)
Would this be good enough to verification?
Result of select count(*) from inbox where username is not null; being equal to
select count(userRefId) from inbox;
Is the data transferred correctly? First, the update looks correct, so you don't really need to worry.
You can get all rows in consumer_inbox where the user names don't match
select ci.*. -- or count(*)
from consumer_inbox ci
where not exists (select 1
from user u
where ci.userRefId = u.userId
);
This doesn't mean that the update didn't work. Just that the values in consumer_inbox have no matches.
Under the circumstances of your code, this is equivalent to:
select ci.*
from consumer_inbox ci
where userId is null;
Although this would not pick up a userId set to a non-matching record (cosmic rays, anyone?).
You can also validate the additional fields used for matching:
select ci.*. -- or count(*)
from consumer_inbox ci
where not exists (select 1
from user u
where ci.userRefId = u.userId and
ci.username = u.username and
ci.businessname = u.businessname
);
However, all this checking seems unnecessary, unless you have trigger on the tables or known non-matched records.

Create an inbox message type of query

I've just started learning SQL, so I apologies if this question is a bit of a dummy question.
I have two tables:
direct_messages
users
A direct_messages table looks like this:
receiverId and senderId are foreign keys to my users table.
What I'm trying to achieve is a typical inbox message query. Meaning get one message of each message sent that has the same receiverId as the logged in user and also get the latest one.
I've tried to get this working almost the whole day and I've come this "far":
SELECT *
FROM "direct_messages" tm
LEFT JOIN "users" tu ON "tm.senderId" = "tu.id"
WHERE "tm.createdAt"
ORDER BY "created_at" DESC
LIMIT 1;
But I'm just getting ERROR: column "tm.senderId" does not exist.
All the help I can get is super appreciated. Thank you!
EDIT:
As requested, I added some example data: https://gist.github.com/Martinnord/f07bc62389f2ecd0df8e6716dd797a15
What I want is to get a list of messages that has a particular receiverId, but only one from each unique senderId and get the latest one. Like a typical message inbox.
As some people mentioned in the comments you have a problem with your double quotes and they explained how you should handle that.
I will show you how to solve the sql problem that you are having.
I will create a sample messages table.
CREATE TABLE messages (
id BIGSERIAL PRIMARY KEY ,
sender_id BIGINT ,
reciever_id BIGINT,
message TEXT,
sent_at TIMESTAMP
);
Now lets fill that table with some data:
INSERT INTO messages (sender_id, reciever_id, message, sent_at) VALUES
(1,3,'User 1 to user 3 1 hour ago', '2017-07-10 15:00:00'),
(1,3,'User 1 to user 3 last','2017-07-10 16:00:00'),
(2,3,'User 2 to user 3 2 hours ago','2017-07-10 14:00:00'),
(2,3,'User 2 to user 3 last','2017-07-10 16:00:00');
Now, lets say that we want to find all of the last mesasges (by sender_id) that were sent to the user with id 3.
So based on the input data, the result should return 2 messages:
From user 1 to user 3 with value "User 1 to user 3 last"
From user 2 to user 3 with value "User 2 to user 3 last"
That can be achieved with the following query:
WITH sender_last_message_time AS (
SELECT sender_id, MAX(sent_at) sent_at FROM messages
WHERE reciever_id = 3
GROUP BY sender_id )
SELECT * FROM messages m
JOIN sender_last_message_time r ON m.sender_id = r.sender_id AND r.sent_at = m.sent_at;
Explanation:
I create the sender_last_message_time CTE that holds the last message time (sent by sender_id) for each sender. Once I have sender_id and the time of the last message for that sender_id, I just join the data with the messages table in order to fetch the messages.
Double quoting everything in Postgresql may be considered harmful because of how double quotes work. They should only be used when there's likely to be confusion with a keyword.
"tm.senderId" does not mean senderId in the tm alias. Because the . is quoted it means exactly the identifier tm.senderId. Postgres is looking for a column tm.senderId in the table direct_messages and not finding it.
Instead you'd write "tm"."senderId" careful to quote the table/alias and column separately.
This leads to the next pitfall, casing. Unless they're quoted, Postgres will lower case columns and table names for you.
test=# create table direct_messages ( senderId integer );
CREATE TABLE
test=# \d direct_messages
Table "public.direct_messages"
Column | Type | Collation | Nullable | Default
----------+---------+-----------+----------+---------
senderid | integer | | |
(Note even the table description "public.direct_messages" is misleading.)
And it will do the same to unquoted tables and columns in queries.
test=# select * from direct_messages tm where tm.sEnDerID is not null;
senderid
----------
(0 rows)
But if you quote them, it will look for an exact match.
test=# select * from direct_messages tm where tm."sEnDerID" is not null;
ERROR: column tm.sEnDerID does not exist
LINE 1: select * from direct_messages tm where tm."sEnDerID" is not ...
test=# select * from direct_messages tm where tm."senderId" is not null;
ERROR: column tm.senderId does not exist
LINE 1: select * from direct_messages tm where tm."senderId" is not ...
^
HINT: Perhaps you meant to reference the column "tm.senderid".
test=# select * from direct_messages tm where tm."senderid" is not null;
senderid
----------
(0 rows)
There's no ambiguous column names in your query, so remove the double quotes.
SELECT *
FROM direct_messages tm
LEFT JOIN users tu ON tm.senderId = tu.id
WHERE tm.createdAt
ORDER BY created_at DESC
LIMIT 1;
(Side note: it's confusing to mix camelCase createdAt and snake_case created_at. Pick one style for the project and stick with it.)

How to design those queries on existing table

Currently I have some existing tables about user action and user info and their associated meta info:
action table:
user_id, action_detail
1 "action_click"
2 "action_drag"
user info tablle
user_id, full_name, email
1 "User One" "userone#user.com"
1 "User Two" "usertwo#user2.com"
company info table
company_name, company_domain
"User Company" "user.com"
"User2 Company" "user2.com"
The new requirement I got is:
Building queries that can find all the actions of:
all users from a single company
a single company but exclude certain users specified
multiple companies together but exclude certain users specified
Could anyone give some thoughts about it( especially what is the efficient way to do 2 and 3)?
Requirement #2 is a subset of requirement #3 (a single company is just a list of companies with the size of one). You could use the exists operator to find users under the companies domain, and exclude users based on other conditions:
SELECT *
FROM user u
WHERE full_name NOT IN ('John Doe', 'Jane Doe' /* or any other condition */) AND
EXISTS (SELECT *
FROM company c
WHERE c.company_name NOT IN ('company1', 'company2', /* etc. */) AND
u.email LIKE '%#' || c.company_domain)
EDIT:
To address the conversation in the comments, if you have a large number of ignored users, you may want to have an auxiliary table of ignored users so you can index them and make the search faster.
E.g.:
CREATE TABLE ignored_users (
full_name VARCHAR PRIMARY KEY
);
INSERT INTO ignored_users VALUES ('John Doe');
-- A bunch of other inserts...
SELECT *
FROM user u
WHERE full_name NOT IN (SELECT full_name FROM ignored_users) AND
EXISTS (SELECT *
FROM company c
WHERE c.company_name NOT IN ('company1', 'company2', /* etc. */) AND
u.email LIKE '%#' || c.company_domain)

Postgres Where query not working with numeric value for character varying data types

Here I'm using postgres 9.6 And PFA is my table schema for postgres
id username
----------
1 ashesh
2 123456
While doing a query on a username with '123456' it will not gives the response but when doing a query with 'ashesh' it will give a response. My query is
select * from customer where username = '123456';
Gives me a blank response but while querying with 'ashesh'
select * from customer where username = 'ashesh';
it will give the response.
Perhaps, sometimes you keep a blank in the username value(it seems for 123456),
trim may be used in the where condition for username column :
create table customer( ID int, username varchar(125));
insert into customer values(1,'ashesh');
insert into customer values(2,'123456 ');
create table customer( ID int, username varchar(125));
insert into customer values(1,'ashesh');
id username
1 ashesh
select * from customer where trim(username) = 'ashesh';
1 ashesh
select * from customer where username = '123456';
--no results return
select * from customer where trim(username) = '123456';
id username
2 123456
SQL Fiddle Demo
Your problem would seem to be hidden characters -- either in the where constant or in the column itself. If the first, then simply re-typing the code should fix the problem.
The second requires more investigation. I would start with:
where username like '%123456%'
to see if any usernames contain that sequence of characters. If this finds the row, then start looking for hidden characters. If not, then try:
where username like '%1%2%3%4%5%6%'
This will look for hidden characters between the digits. Once you have determined what the issue is, you can figure out how to deal with the problem.
If you have a fixed set of characters that are allowed, you can also test:
where regexp_replace(username, '[^a-zA-Z0-9]', '', 'g') = '123456'
If this is the problem, you can remove the hidden characters with:
update customer
set username = regexp_replace(username, '[^a-zA-Z0-9]', '', 'g')
where username ~ '[^a-zA-Z0-9]';

SQL query to create a single record view of multiple records - Dynamic Stored Procedure?

Ok, I think there must be an easier way to do this, I would like to do it dynamically instead of hand writing every line. I am more familiar with MySQL than SQL Server 2008, but I'm not sure how I would do this in MySQL either.
I have 3 tables.
Table 1: USER id | email | password
Table 2: METADATA id | name (list of fields I need to know about user)
Table 3: USER_META id | uid | name | value (where I store the user meta data)
I do not hard code the METADATA because it changes for each instance of this application. but in this example, lets just say the meta data is "eyes", "phone" , "city" (there are actually many more, there might be 20 this week and 40 next week)
So the way it works is when a user registers, I build the registration form using the METADATA table to build the form. It creates a form with "eyes" , "phone" and "city".
When I save the form, I save a single record into the USER_META table for each data item.
So when registering, I get 3 inserts:
insert into USER_META(uid,name,value) values (5,"eyes","brown")
insert into USER_META(uid,name,value) values (5,"phone","555-1212")
insert into USER_META(uid,name,value) values (5,"city","San Francisco")
Now, I want to write a Stored Procedure that will return a record like this"
EXECUTE Get_Meta 5
returns:
uid | email | eyes | phone | city
5 x#x.com brown 555-1212 San Francisco;
I could write a long hard coded select statement like:
DECLARE #UID int;
SELECT id, email,
(select value from USER_META
where uid = #UID and name = 'EYES') as EYES,
(select value from USER_META
where uid = #UID and name = 'PHONE') as PHONE,
(select value from USER_META
where uid = #UID and name = 'CITY') as CITY,
FROM USER
where id = #UID;
But that kind of defeats the whole purpose, and I have to write it again every week whenever the metadata requirements change (eg when we launch another instance).
I would like to have something like this: (forgive me I am not very experienced with Advanced SQL, so maybe this is easy and I just don't know how to do it?) I will write it in basic code terms to try and explain my thinking:
DECLARE #UID int;
DECLARE #META_NAMES array;
#META_NAMES = (select NAME from METADATA);
SELECT id, email,
for each (#META_NAMES as $THIS_NAME) {
(select value from USER_META
where uid = #UID and name = '$THIS_NAME') as $THIS_NAME,
}
FROM USER
where id = #UID;
Is there a way to write this in SQL Server 2008?
You would USE FOR XML_PATH in SQL Server. A workaround for GROUP_CONCAT
SELECT
U.UID,
U.EMAIL,
stuff(
(
select cast(',' as varchar(max)) + name + '=' + Value
from USER_META
WHERE UID = U.UID
for xml path('')
), 1, 1, '') AS M
FROM
USERS U
SQL Fiddle