How to make sql (postgresql) query? - sql

/* Create a table called tbl1 */
CREATE TABLE tbl1(id integer PRIMARY KEY, name text, lang text);
/* Create few records in this table */
INSERT INTO tbl1 VALUES(1,'John', 'Perl');
INSERT INTO tbl1 VALUES(2,'Pete', 'Perl');
INSERT INTO tbl1 VALUES(3,'Sam', 'Java');
INSERT INTO tbl1 VALUES(4,'John', 'Php');
INSERT INTO tbl1 VALUES(5,'Sam', 'Perl');
INSERT INTO tbl1 VALUES(6,'Sam', 'Php');
INSERT INTO tbl1 VALUES(7,'Pete', 'C');
INSERT INTO tbl1 VALUES(8,'Bob', 'Java');
Derive the names of those who know Perl and Php.

The OP contained very Little information. Is this what you need?
SELECT Name
FROM tbl1
WHERE lang = 'Perl' AND lang = 'Php'
For future reference; you really should provide a minimal working example, as you have done here, but also what you have actually tried, which you lacked.
If this query is what you need, then it is very straight-forward and your problem should have been managable with just googling some minutes.

SELECT distinct(name)
FROM tbl1
WHERE name IN (SELECT name FROM tbl1 WHERE lang = 'Perl')
AND name IN (SELECT name FROM tbl1 WHERE lang = 'Php');
Is it good answer? I don't think so.
I want to find better way.
2 additional answers
SELECT n FROM (
SELECT tbl1.name, string_agg(tbl1.lang, '-')
FROM tbl1
GROUP BY name) AS t (n, l)
WHERE
position('Perl' in l) <> 0
AND
position('Php' in l) <> 0;
===================================
SELECT t1.name FROM (
SELECT name FROM tbl1 WHERE lang = 'Perl'
) AS t1, (
SELECT name FROM tbl1 WHERE lang = 'Php'
) AS t2
WHERE t1.name = t2.name;

Related

Provide the values for a 'like' function from a specific column in a Table?

I am using SQL Server 2014 and I need a T-SQL query which uses the like function to run on a specific column (c1) of a Table (t1) to find out if it contains one of the codes from a list of codes found in the column (c2) of another Table (t2).
To simplify, here is the scenario and the expected output:
Table t1:
ID Notes
101 (free text in this column)
102 ...
... ...
115000 ...
Table t2 (list of more than 300 codes):
Code
FR110419
GB150619
...
DE111219
What I am looking for:
SELECT ID
FROM t1
WHERE t1.Notes like (SELECT Code FROM t2)
Since the like operator needs '%'to work, I am confused as to how to construct that line.
I have done some research on StackOverflow and the closest solution I have come across is for a mysql problem: how to use LIKE with column name
Any type of help will be most appreciated.
You seem to be looking for a JOIN:
SELECT ID
FROM t1
INNER JOIN t2 ON t1.Notes LIKE '%' + t2.Code + '%'
If different Codes might appear in the same Note, using an EXISTS condition with a correlated subquery is also an option, as it would avoid duplicating records in the output:
SELECT ID
FROM t1
WHERE EXISTS (
SELECT 1 FROM t2 WHERE t1.Notes LIKE '%' + t2.Code + '%'
)
You can use cross apply with charindex like this:
--Loading data
create table t1 (id varchar(10));
insert into t1 (id) values ('100100'),('200100'),('300100')
insert into t1 (id) values ('100200'),('200200'),('300200')
insert into t1 (id) values ('100300'),('200300'),('300300')
insert into t1 (id) values ('0100'),('0200'),('0300')
insert into t1 (id) values ('00010'),('00020'),('00030')
create table t2 (id varchar(10));
insert into t2 (id) values ('020'),('010')
select t.id
from t1 as t
cross apply t2 as t2
--where charindex(t2.id,t.id) > 0 -- simulates a double % one at the beginning and one at the end
--where charindex(t2.id,t.id) = 1 -- simulates a % at the beginning
where charindex(t2.id,t.id) = len(t.id)-len(t2.id)+1 -- simulates a % at the end
The only thing is that the table is very big this could be a slow solution.
Building on what's already been posted, you can create an indexed view to really speed things up.
Using CTE6's sample data...
--Loading data
create table t1 (id varchar(10));
insert into t1 (id) values ('100100'),('200100'),('300100')
insert into t1 (id) values ('100200'),('200200'),('300200')
insert into t1 (id) values ('100300'),('200300'),('300300')
insert into t1 (id) values ('0100'),('0200'),('0300')
insert into t1 (id) values ('00010'),('00020'),('00030')
create table t2 (id varchar(10));
insert into t2 (id) values ('020'),('010')
GO
-- The View
CREATE VIEW dbo.vw_t1t2 WITH SCHEMABINDING AS
SELECT t1 = t1.id, t2 = t2.id, cb = COUNT_BIG(*)
FROM dbo.t1 AS t1
CROSS JOIN dbo.t2 AS t2
WHERE CHARINDEX(t2.id,t1.id) > 0
GROUP BY t1.id, t2.id
GO
-- The index (may need to add something else to make UNIQUE)
CREATE UNIQUE CLUSTERED INDEX uq_cl_vwt1t2 ON dbo.vw_t1t2(t1,t2);
GO
This will perform very well for SELECT statements but could impact data modifications against t1 and t2 so make sure to use the smallest datatype possible and only include columns you are certain you need (Varchar(10) is good). I include COUNT_BIG() because it's required in indexed views that leverage GROUP BY.

unable to get a stable set of rows error

I am trying to perform a merge into a table (let's call it table1) from a table2. In the USING condition I need a third table (table3). This third table contains some IDs that I need in table1. A simplified version of my merge looks like:
MERGE INTO table1 a
USING (
SELECT ID, address
FROM table3 b
Where address IN
(
SELECT address
FROM table3
WHERE address IS NOT NULL
AND ID> 0
GROUP BY address
HAVING COUNT(*) = 1
)
) c
ON (a.address = c.address)
WHEN MATCHED THEN
UPDATE SET a.ID = c.ID
WHERE a.ID = 0
I know that the error I get is usually caused by the query in the USING clause, but theoretically this problem should be eliminated by the count(*)=1 condition.
I have duplicates in table2, but they should all get an ID from table3 or ID 0 if the address is duplicated in table3.
IDs are unique for an address, so they should be distinct.
P.S. This merge is performed automatically by a script that , so I can modify the query to add more conditions/restrictions, but I cannot change the structure [meaning I have to use these 3 tables as they are].
I hope this makes sense.
Any ideas why this still does not work for me?
Try this:
MERGE INTO table1 a
USING (
SELECT max(ID), address
FROM table3 b
WHERE address IS NOT NULL AND ID > 0
GROUP BY address
HAVING COUNT(*) = 1
) c
ON (a.address = c.address)
WHEN MATCHED THEN
UPDATE SET a.ID = c.ID
WHERE a.ID = 0;
you have where condition in inner query but not in outer query. If you want your original query please try:
MERGE INTO table1 a
USING (
SELECT ID, address
FROM table3 b
AND address IN
(
SELECT address
FROM table3
WHERE address IS NOT NULL
AND ID> 0
GROUP BY address
HAVING COUNT(*) = 1
)
WHERE address IS NOT NULL
AND ID> 0
) c
ON (a.address = c.address)
WHEN MATCHED THEN
UPDATE SET a.ID = c.ID
WHERE a.ID = 0
The issue is more than likely due to the duplicate rows from table2. Here's a simple test case demonstrating the issue:
Setup:
CREATE TABLE t1 (ID INTEGER PRIMARY KEY,
val VARCHAR2(1));
CREATE TABLE t2 (ID INTEGER,
val VARCHAR2(1));
INSERT INTO t1 (ID, val) VALUES (1, 'A');
INSERT INTO t2 (ID, val) VALUES (1, 'B');
INSERT INTO t2 (ID, val) VALUES (1, 'B');
COMMIT;
Merge that will error:
MERGE INTO t1 USING t2
ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET t1.val = t2.val;
ORA-30926: unable to get a stable set of rows in the source tables
Merge that will succeed:
MERGE INTO t1 USING (SELECT DISTINCT id, val FROM t2) t2
ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET t1.val = t2.val;
N.B. The second merge will still fail if you have different values returned for val for the same id; that means you will have more than one row returned for a given id, and Oracle won't know which one to use to update the target table with.
In order to make sure your merge statement will work, you will need to ensure that you will return at most 1 row per address in the source subquery.

JOIN - Get one full table and the rest of another

Hi guys here is my Schema: http://sqlfiddle.com/#!4/82771
CREATE TABLE t1
(
Age INT,
Name VARCHAR(20)
);
CREATE TABLE t2
(
Age INT,
Name VARCHAR(20)
);
INSERT INTO t1(Age, Name) VALUES(31, NULL);
INSERT INTO t1(Age, Name) VALUES(32, NULL);
INSERT INTO t1(Age, Name) VALUES(33, NULL);
INSERT INTO t1(Age, Name) VALUES(34, NULL);
INSERT INTO t2(Age, Name) VALUES(31, 'Panos');
I need a Join query that will give me this Result:
Age Name
31 'Panos'
32 Null
33 Null
34 Null
I've tried LEFT JOIN and RIGHT JOIN on Age but I can't get what I need. It should be pretty simple but its just not coming to me...
maybe you have missed something when you are doing LEFT JOIN. Use also coalesce to return the first non-null value.
SELECT t1.Age, COALESCE(t2.name, t1.Name) Name
FROM t1
LEFT JOIN t2
ON t1.Age = t2.Age
SQLFiddle Demo
One way is to use union all and then check for non-existence in the second table:
select *
from t1
union all
select *
from t2
where Not exists (select 1 from t1 where t1.age = t2.age)

How to remove duplicate records in a table?

I've got a table in a testing DB that someone apparently got a little too trigger-happy on when running INSERT scripts to set it up. The schema looks like this:
ID UNIQUEIDENTIFIER
TYPE_INT SMALLINT
SYSTEM_VALUE SMALLINT
NAME VARCHAR
MAPPED_VALUE VARCHAR
It's supposed to have a few dozen rows. It has about 200,000, most of which are duplicates in which TYPE_INT, SYSTEM_VALUE, NAME and MAPPED_VALUE are all identical and ID is not.
Now, I could probably make a script to clean this up that creates a temporary table in memory, uses INSERT .. SELECT DISTINCT to grab all the unique values, TRUNCATE the original table and then copy everything back. But is there a simpler way to do it, like a DELETE query with something special in the WHERE clause?
You don't give your table name but I think something like this should work. Just leaving the record which happens to have the lowest ID. You might want to test with the ROLLBACK in first!
BEGIN TRAN
DELETE <table_name>
FROM <table_name> T1
WHERE EXISTS(
SELECT * FROM <table_name> T2
WHERE
T1.TYPE_INT = T2.TYPE_INT AND
T1.SYSTEM_VALUE = T2.SYSTEM_VALUE AND
T1.NAME = T2.NAME AND
T1.MAPPED_VALUE = T2.MAPPED_VALUE AND
T2.ID > T1.ID
)
SELECT * FROM <table_name>
ROLLBACK
here is a great article on that: Deleting duplicates, which basically uses this pattern:
WITH q AS
(
SELECT d.*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY value) AS rn
FROM t_duplicate d
)
DELETE
FROM q
WHERE rn > 1
SELECT *
FROM t_duplicate
WITH Duplicates(ID , TYPE_INT, SYSTEM_VALUE, NAME, MAPPED_VALUE )
AS
(
SELECT Min(Id) ID TYPE_INT, SYSTEM_VALUE, NAME, MAPPED_VALUE
FROM T1
GROUP BY TYPE_INT, SYSTEM_VALUE, NAME, MAPPED_VALUE
HAVING Count(Id) > 1
)
DELETE FROM T1
WHERE ID IN (
SELECT T1.Id
FROM T1
INNER JOIN Duplicates
ON T1.TYPE_INT = Duplicates.TYPE_INT
AND T1.SYSTEM_VALUE = Duplicates.SYSTEM_VALUE
AND T1.NAME = Duplicates.NAME
AND T1.MAPPED_VALUE = Duplicates.MAPPED_VALUE
AND T1.Id <> Duplicates.ID
)

Avoid duplicates in INSERT INTO SELECT query in SQL Server

I have the following two tables:
Table1
----------
ID Name
1 A
2 B
3 C
Table2
----------
ID Name
1 Z
I need to insert data from Table1 to Table2. I can use the following syntax:
INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1
However, in my case, duplicate IDs might exist in Table2 (in my case, it's just "1") and I don't want to copy that again as that would throw an error.
I can write something like this:
IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1
Is there a better way to do this without using IF - ELSE? I want to avoid two INSERT INTO-SELECT statements based on some condition.
Using NOT EXISTS:
INSERT INTO TABLE_2
(id, name)
SELECT t1.id,
t1.name
FROM TABLE_1 t1
WHERE NOT EXISTS(SELECT id
FROM TABLE_2 t2
WHERE t2.id = t1.id)
Using NOT IN:
INSERT INTO TABLE_2
(id, name)
SELECT t1.id,
t1.name
FROM TABLE_1 t1
WHERE t1.id NOT IN (SELECT id
FROM TABLE_2)
Using LEFT JOIN/IS NULL:
INSERT INTO TABLE_2
(id, name)
SELECT t1.id,
t1.name
FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
WHERE t2.id IS NULL
Of the three options, the LEFT JOIN/IS NULL is less efficient. See this link for more details.
In MySQL you can do this:
INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1
Does SQL Server have anything similar?
I just had a similar problem, the DISTINCT keyword works magic:
INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
I was facing the same problem recently...
Heres what worked for me in MS SQL server 2017...
The primary key should be set on ID in table 2...
The columns and column properties should be the same of course between both tables. This will work the first time you run the below script. The duplicate ID in table 1, will not insert...
If you run it the second time, you will get a
Violation of PRIMARY KEY constraint error
This is the code:
Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Using ignore Duplicates on the unique index as suggested by IanC here was my solution for a similar issue, creating the index with the Option WITH IGNORE_DUP_KEY
In backward compatible syntax
, WITH IGNORE_DUP_KEY is equivalent to WITH IGNORE_DUP_KEY = ON.
Ref.: index_option
From SQL Server you can set a Unique key index on the table for (Columns that needs to be unique)
A little off topic, but if you want to migrate the data to a new table, and the possible duplicates are in the original table, and the column possibly duplicated is not an id, a GROUP BY will do:
INSERT INTO TABLE_2
(name)
SELECT t1.name
FROM TABLE_1 t1
GROUP BY t1.name
In my case, I had duplicate IDs in the source table, so none of the proposals worked. I don't care about performance, it's just done once.
To solve this I took the records one by one with a cursor to ignore the duplicates.
So here's the code example:
DECLARE #c1 AS VARCHAR(12);
DECLARE #c2 AS VARCHAR(250);
DECLARE #c3 AS VARCHAR(250);
DECLARE MY_cursor CURSOR STATIC FOR
Select
c1,
c2,
c3
from T2
where ....;
OPEN MY_cursor
FETCH NEXT FROM MY_cursor INTO #c1, #c2, #c3
WHILE ##FETCH_STATUS = 0
BEGIN
if (select count(1)
from T1
where a1 = #c1
and a2 = #c2
) = 0
INSERT INTO T1
values (#c1, #c2, #c3)
FETCH NEXT FROM MY_cursor INTO #c1, #c2, #c3
END
CLOSE MY_cursor
DEALLOCATE MY_cursor
I used a MERGE query to fill a table without duplications.
The problem I had was a double key in the tables ( Code , Value ) ,
and the exists query was very slow
The MERGE executed very fast ( more then X100 )
examples for MERGE query
For one table it works perfectly when creating one unique index from multiple field. Then simple "INSERT IGNORE" will ignore duplicates if ALL of 7 fields (in this case) will have SAME values.
Select fields in PMA Structure View and click Unique, new combined index will be created.
A simple DELETE before the INSERT would suffice:
DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1
Switching Table1 for Table2 depending on which table's Id and name pairing you want to preserve.