Oracle simple list results to comma separated list with quotes? - sql

I would like to get results of this simple query
select PersonID from Persons where city ='Miami'
as a flat comma separated list with quotes.
desired results should be '3','5','7','8'
http://sqlfiddle.com/#!4/95e86d/1/0
I tried list_add but im getting: ORA-00904: "STRING_AGG": invalid identifier
CREATE TABLE Persons (
PersonID NUMBER,
FirstName varchar(255),
City varchar(255)
);
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (1, 'Tom B.','Stavanger');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (2, 'Jerry M.','Train City');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (3, 'Eric g.','Miami');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (4, 'Bar Y.','Manhattan');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (5, 'John K.','Miami');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (6, 'Foo F.','Washington');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (7, 'Alen D.','Miami');
INSERT INTO Persons (PersonID, FirstName, City)
VALUES (8, 'John K.','Miami');

select listagg( '''' || PersonID || '''', ',' )
within group (order by personID)
from Persons
where city ='Miami'
should work.
I'd have to question why you're trying to produce this particular result. If you're going to generate this string so that you can subsequently use it in the IN list of another dynamically generated SQL statement, you're probably better off taking a step back because there are much better ways to solve the problem.
SQL Fiddle example

Related

How to track the previous row before the item in the row equals ceratin value

I have the following dummy data:
create table scientist (id integer, firstname varchar(100), lastname varchar(100));
insert into scientist (id, firstname, lastname) values (1, 'albert', 'einstein');
insert into scientist (id, firstname, lastname) values (2, 'isaac', 'einstein');
insert into scientist (id, firstname, lastname) values (3, 'marie', 'einstein');
insert into scientist (id, firstname, lastname) values (4, 'bob', 'einstein');
insert into scientist (id, firstname, lastname) values (0, 'lola', 'cur');
insert into scientist (id, firstname, lastname) values (4, 'mb', 'cur');
insert into scientist (id, firstname, lastname) values (5, 'peter', 'cur');
select * from scientist;
and I want to get everything before firstname = marie for rows, where lastname = 'einstein' and everuthing that is before firstname mb e.g. where lastname = 'cur'. I assume it can be done by row number, at first count the row number for the row with item which equals to the certain data, after that delete everything where row number is bigger. But the problem is that it will delete everything with the lastname = 'cur' and so on(I have other lastnames).Can it be solved or no?
Desired output are first,second and fifth rows
here is one way :
select * from (
select *
, count(case when lastname ='cur' and firstname = 'mb' then 1
when lastname ='einstein' and firstname = 'marie' then 1 end) over (partition by lastname order by id) cnt
from scientist s
) t
where cnt = 0

SQL Server ranking weirdness using FREETEXTTABLE across multiple columns

I have been struggling to get my head around how SQL Server full text search ranks my results.
Consider the following FREETEXTTABLE search:
DECLARE #SearchTerm varchar(55) = 'Peter Alex'
SELECT ftt.[RANK], v.*
FROM FREETEXTTABLE (vMembersFTS, (Surname, FirstName, MiddleName, MemberRef, Passport), #SearchTerm) ftt
INNER JOIN vMembersFTS v ON v.ID = ftt.[KEY]
ORDER BY ftt.[RANK] DESC;
This returns the following results and rankings:
RANK ID MemberRef Passport FirstName MiddleName Surname Salutation
----- ---- ---------- ----------- ----------- ------------ ---------- ------------
18 2 AB-002 Pete Peters
18 9 AB-006 George Alex Mr Alex
18 13 AB-009 Peter David Alex Mr Alex
14 3 AB-003 Peter Alex Jones
As you may be able to tell from the results posted above, the last row, although having, what I consider, a good match on both 'Peter' and 'Alex', appears with a rank of only 14 where the result in the first row has only a single match on 'Peter' (admittedly the surname is 'Peters').
This is a contrived example, but goes some way to illustrate my frustrations and lack of knowledge.
I have spent quite a bit of time researching, but I am feeling a bit out of my depth now. I'm sure that I'm doing something stupid such as searching across multiple columns.
I welcome your help and support. Thanks in advance.
Thanks,
Kaine
(BTW I am using SQL Server 2012)
Here is the SQL you can use to repeat the test yourself:
-- Create the Contacts table.
CREATE TABLE dbo.Contacts
(
ID int NOT NULL PRIMARY KEY,
FirstName varchar(55) NULL,
MiddleName varchar(55) NULL,
Surname varchar(55) NOT NULL,
Salutation varchar(55) NULL,
Passport varchar(55) NULL
);
GO
-- Create the Members table.
CREATE TABLE dbo.Members
(
ContactsID int NOT NULL PRIMARY KEY,
MemberRef varchar(55) NOT NULL
);
GO
-- Create the FTS view.
CREATE VIEW dbo.vMembersFTS WITH SCHEMABINDING AS
SELECT c.ID,
m.MemberRef,
ISNULL(c.Passport, '') AS Passport,
ISNULL(c.FirstName, '') AS FirstName,
ISNULL(c.MiddleName, '') AS MiddleName,
c.Surname,
ISNULL(c.Salutation, '') AS Salutation
FROM dbo.Contacts c
INNER JOIN dbo.Members AS m ON m.ContactsID = c.ID
GO
-- Create the view index for FTS.
CREATE UNIQUE CLUSTERED INDEX IX_vMembersFTS_ID ON dbo.vMembersFTS (ID);
GO
-- Create the FTS catalogue and stop-list.
CREATE FULLTEXT CATALOG ContactsFTSCatalog WITH ACCENT_SENSITIVITY = OFF;
CREATE FULLTEXT STOPLIST ContactsSL FROM SYSTEM STOPLIST;
GO
-- Create the member full-text index.
CREATE FULLTEXT INDEX ON dbo.vMembersFTS
(Surname, Firstname, MiddleName, Salutation, MemberRef, Passport)
KEY INDEX IX_vMembersFTS_ID
ON ContactsFTSCatalog
WITH STOPLIST = ContactsSL;
GO
-- Insert some data.
INSERT INTO Contacts VALUES (1, 'John', NULL, 'Smith', NULL, NULL);
INSERT INTO Contacts VALUES (2, 'Pete', NULL, 'Peters', NULL, NULL);
INSERT INTO Contacts VALUES (3, 'Peter', 'Alex', 'Jones', NULL, NULL);
INSERT INTO Contacts VALUES (4, 'Philip', NULL, 'Smith', NULL, NULL);
INSERT INTO Contacts VALUES (5, 'Harry', NULL, 'Dukes', NULL, NULL);
INSERT INTO Contacts VALUES (6, 'Joe', NULL, 'Jones', NULL, NULL);
INSERT INTO Contacts VALUES (7, 'Alex', NULL, 'Phillips', 'Mr Phillips', NULL);
INSERT INTO Contacts VALUES (8, 'Alexander', NULL, 'Paul', 'Alex', NULL);
INSERT INTO Contacts VALUES (9, 'George', NULL, 'Alex', 'Mr Alex', NULL);
INSERT INTO Contacts VALUES (10, 'James', NULL, 'Castle', NULL, NULL);
INSERT INTO Contacts VALUES (11, 'John', NULL, 'Alexander', NULL, NULL);
INSERT INTO Contacts VALUES (12, 'Robert', NULL, 'James', 'Mr James', NULL);
INSERT INTO Contacts VALUES (13, 'Peter', 'David', 'Alex', 'Mr Alex', NULL);
INSERT INTO Members VALUES (1, 'AB-001');
INSERT INTO Members VALUES (2, 'AB-002');
INSERT INTO Members VALUES (3, 'AB-003');
INSERT INTO Members VALUES (5, 'AB-004');
INSERT INTO Members VALUES (8, 'AB-005');
INSERT INTO Members VALUES (9, 'AB-006');
INSERT INTO Members VALUES (11, 'AB-007');
INSERT INTO Members VALUES (12, 'AB-008');
INSERT INTO Members VALUES (13, 'AB-009');
-- Run the FTS query.
DECLARE #SearchTerm varchar(55) = 'Peter Alex'
SELECT ftt.[RANK], v.*
FROM FREETEXTTABLE (vMembersFTS, (Surname, FirstName, MiddleName, MemberRef, Passport), #SearchTerm) ftt
INNER JOIN vMembersFTS v ON v.ID = ftt.[KEY]
ORDER BY ftt.[RANK] DESC;
The rank is assigning based on the order in your query:
DECLARE #SearchTerm varchar(55) = 'Peter Alex'
SELECT ftt.[RANK], v.*
FROM FREETEXTTABLE (vMembersFTS, (Surname, FirstName, MiddleName, MemberRef, Passport), #SearchTerm) ftt
INNER JOIN vMembersFTS v ON v.ID = ftt.[KEY]
ORDER BY ftt.[RANK] DESC;
So in your case, a match on SurName trumps FirstName, and both trump MiddleName.
Your top 3 results have a rank of 18 as all three match on Surname. The last record has a rank of 14 for matching on FirstName and MiddleName but not SurName.
You can find details on the rank calculations here: https://technet.microsoft.com/en-us/library/ms142524(v=sql.105).aspx
If you want to allocate equal weight to these you can, but you'd have to use CONTAINSTABLE and not FREETEXTTABLE.
Info can be found here: https://technet.microsoft.com/en-us/library/ms189760(v=sql.105).aspx

What is wrong with this JOIN statement in SQL?

I am working on this tutorial about SQL. In Step 2 of 2, it is asked to use the JOIN command to link 2 people from the persons list using information from the friends list. I edited the provided code and obtained the code included below: the relevant part starts with select persons.fullname, persons2.fullname. However, the last paragraph of the code ('relevant code') does not yield any result nor error message. Am I doing something wrong?
Code
CREATE TABLE persons (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fullname TEXT,
age INTEGER);
INSERT INTO persons (fullname, age) VALUES ("Bobby McBobbyFace", "12");
INSERT INTO persons (fullname, age) VALUES ("Lucy BoBucie", "25");
INSERT INTO persons (fullname, age) VALUES ("Banana FoFanna", "14");
INSERT INTO persons (fullname, age) VALUES ("Shish Kabob", "20");
INSERT INTO persons (fullname, age) VALUES ("Fluffy Sparkles", "8");
CREATE table hobbies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
person_id INTEGER,
name TEXT);
INSERT INTO hobbies (person_id, name) VALUES (1, "drawing");
INSERT INTO hobbies (person_id, name) VALUES (1, "coding");
INSERT INTO hobbies (person_id, name) VALUES (2, "dancing");
INSERT INTO hobbies (person_id, name) VALUES (2, "coding");
INSERT INTO hobbies (person_id, name) VALUES (3, "skating");
INSERT INTO hobbies (person_id, name) VALUES (3, "rowing");
INSERT INTO hobbies (person_id, name) VALUES (3, "drawing");
INSERT INTO hobbies (person_id, name) VALUES (4, "coding");
INSERT INTO hobbies (person_id, name) VALUES (4, "dilly-dallying");
INSERT INTO hobbies (person_id, name) VALUES (4, "meowing");
CREATE table friends (
id INTEGER PRIMARY KEY AUTOINCREMENT,
person1_id INTEGER,
person2_id INTEGER);
INSERT INTO friends (person1_id, person2_id)
VALUES (1, 4);
INSERT INTO friends (person1_id, person2_id)
VALUES (2, 3);
/* personal contribution starts here
select persons.fullname,hobbies.name
from persons
join hobbies
on hobbies.person_id=persons.id;
/* select persons.fullname, persons2.fullname
from persons
join persons persons2
join friends
on persons.fullname=friends.person1_id and persons2.fullname=friends.person2_id; */
/* relevant code: */
select persons.fullname, persons2.fullname
from persons
join friends
on persons.fullname=friends.person1_id
join persons persons2
on persons2.fullname=friends.person2_id;
[tutorial (c) khanacademy.org]
join friends
on persons.fullname=friends.person1_id
You're only including friends whose person1_id equals a person's fullname. Since person1_id is an integer and fullname is text, those will never be equal. You probably want:
select persons.fullname, persons2.fullname
from persons
join friends
on persons.id=friends.person1_id
join persons persons2
on persons2.id=friends.person2_id;

Display 2 columns for each header

In SQL Server 2008 I have a table People (Id, Gender, Name).
Gender is either Male or Female. There can be many people with the same name.
I would like to write a query that displays for each gender the top 2 names
by count and their count, like this:
Male Female
Adam 23 Rose 34
Max 20 Jenny 15
I think that PIVOT might be used but all the examples I have seen display only one column for each header.
Here is an example on SQL Fiddle -- http://sqlfiddle.com/#!3/b3477/1
This uses an couple of common table expressions to separate the genders.
create table People
(
Id int,
Gender varchar(50),
Name varchar(50)
)
;
insert into People values (1, 'Male', 'Bob');
insert into People values (2, 'Male', 'Bob');
insert into People values (3, 'Male', 'Bill');
insert into People values (4, 'Male', 'Chuck');
insert into People values (5, 'Female', 'Anne');
insert into People values (6, 'Female', 'Anne');
insert into People values (7, 'Female', 'Bobbi');
insert into People values (8, 'Female', 'Jane');
with cteMale as
(
select Name as 'MaleName', Count(*) as Num, ROW_NUMBER() over(order by count(*) desc, Name) RowNum
from People
where Gender = 'Male'
group by Name
)
,
cteFemale as
(
select top 2 Name as 'FemaleName', Count(*) as Num, ROW_NUMBER() over(order by count(*) desc, Name) RowNum
from People
where Gender = 'Female'
group by Name
)
select a.MaleName, a.Num as MaleNum, b.femaleName, b.Num as FemaleNum
from cteMale a
join cteFemale b on
a.RowNum = b.RowNum
where a.RowNum <= 2
Use a windowing function. Below is a complete solution using a temporary table #people.
-- use temp db
use tempdb;
go
-- drop test table
--drop table #people;
--go
-- create test table
create table #people (my_id int, my_gender char(1), my_name varchar(25));
go
-- clear test table
delete from #people;
-- three count
insert into #people values
(23, 'M', 'Adam'),
(34, 'F', 'Rose');
go 3
-- two count
insert into #people values
(20, 'M', 'Max'),
(15, 'F', 'Jenny');
go 2
-- one count
insert into #people values
(20, 'M', 'John'),
(15, 'F', 'Julie');
go
-- grab top two by gender
;
with cte_Get_Top_Two as
(
select ROW_NUMBER() OVER(PARTITION BY my_gender ORDER BY count() DESC) AS my_window,
my_gender, my_name, count() as total
from #people
group by my_gender, my_name
)
select * from cte_Get_Top_Two where my_window in (1, 2)
go
Here is the output.
PS: You can drop my_id from the table since it does not relate to your problem but does not change solution.

Better way SQL insert query

Actually, I don't know what is different the following query?
Which one is better(performance, etc...)? Btw, I use SQL Server.
Query 1 :
INSERT INTO PERSON (ID, NAME, ADDRESS) VALUES('001', 'Smit', 'London');
INSERT INTO PERSON (ID, NAME, ADDRESS) VALUES('002', 'Jhon', 'London');
Query 2 : I never saw before
INSERT INTO PERSON (ID, NAME, ADDRESS)
SELECT '001', 'Smit', 'London' UNION ALL
SELECT '002', 'Jhon', 'London'
How about the multi-row syntax with table value constructors:
INSERT INTO PERSON (ID, NAME, ADDRESS)
VALUES ('001', 'Smit', 'London'), ('002', 'Jhon', 'London');