SQL select query order by on where in - sql

How Can I make the order by based on what I input on where?
example query
select * from student where stud_id in (
'5',
'3',
'4'
)
the result would be
id| name |
5 | John |
3 | Erik |
4 | Michael |
Kindly help me thanks.

One method is with a derived table:
select s.*
from student s cross join
(values (5, 1), (3, 2), (4, 3)
) v(stud_id, ord)
on v.stud_id = s.stud_in
order by v.ord;
stud_id looks like a number so I dropped the single quotes. Numbers should be compared to numbers. If it is really a string, then use the single quotes.

As Gordon mentioned, you need something to provide order. An IN clause doesn't have a pre-defined order, just like a table doesn't. Rather than numbering the row order yourself, you could have a table variable do it like this:
DECLARE TABLE #StudentIDs
(
StudentIDKey int IDENTITY(1,1) PRIMARY KEY,
StudentID int
);
INSERT #StudentIDs (StudentID)
VALUES
(5),
(3),
(4);
SELECT *
FROM Student AS s
INNER JOIN #StudentIDs AS id
ON s.StudentID = id.StudentID
ORDER BY id.StudentIDKey;
That should be far easier if you have a lot of values to work with.
Hope that helps.

Related

How to populate column with list from another table?

Suppose I have three tables: STORES, STATES, and STORES_STATES.
STORES contains records of individual stores
STATES just contains a list of all 50 US states
STORES_STATES is a join table that contains pairs of stores and states to show which stores are available in which states
Consider the following tables (can't figure out how to format an ASCII table here):
What I need is a SELECT statement that will return each store in one column, and a list of states in another:
How do I combine the results of a subquery into a single column like this?
I would imagine it would be similar to this query:
SELECT
STORE_NAME,
(SELECT STATE_ABV FROM STORES_STATES WHERE STORES_STATES.STORE_ID = STORES.ID)
FROM STORES;
But this obviously fails since the subquery returns more than one result.
You can use APPLY :
SELECT s.store_name, STUFF(ss.state_abv, 1, 1, '') AS States
FROM stores s CROSS APPLY
( SELECT ', '+ss.state_abv
FROM stores_state ss
WHERE ss.store_id = s.id
FOR XML PATH('')
) ss(state_abv);
your two options are either the STRING_AGG function in the newest versions of SQL Server, or using an XML concatenation technique as described in this answer:
How to concatenate text from multiple rows into a single text string in SQL server?
The XML method is messy-looking in your code and difficult to remember - I always have to look up the syntax - but it's actually quite fast.
You can use STUFF() function along side with FOR XML PATH('') and join both tables on StoreID
CREATE TABLE Stores
(
ID INT,
StoreName VARCHAR(45)
);
CREATE TABLE States
(
StoreID INT,
StateABV VARCHAR(45)
);
INSERT INTO Stores VALUES
(1, 'Wolmart'), (2, 'Costco'), (3, 'Croegers');
INSERT INTO States VALUES
(1, 'NY'),
(1, 'WY'),
(1, 'MI'),
(2, 'AL'),
(2, 'GA'),
(2, 'TX'),
(3, 'FL');
SELECT SR.StoreName,
STUFF(
(
SELECT ',' + ST.StateABV
FROM States ST
WHERE ST.StoreID = SR.ID
FOR XML PATH('')
), 1, 1, ''
) State
FROM Stores SR;
Returns:
+-----------+----------+
| StoreName | State |
+-----------+----------+
| Wolmart | NY,WY,MI |
| Costco | AL,GA,TX |
| Croegers | FL |
+-----------+----------+

Nested query that requires the first result to be returned

I have 2 tables as such
Table ErrorCodes:
type_code desc
01 Error101
02 Error99
03 Error120
Table ErrorXML:
row_index typeCode
1 87
2 02
3 01
The output should be the description(column desc) of the first matched type_code between the 2 tables
Expected output : Error99
I have gotten so far.
select isnull(descript, 'unknown') as DESCRIPTION
from (select top 1 a.stmt_cd as descript
from ErrorCodes a, ErrorXML b
where a.type_cd = b.typecode
order by b.row_index)
But this query doesn't return the string UNKNOWN when there is no common typecode (join condition) between the 2 tables. In this case, im getting null.
How can I resolve this?
This is an interesting question. I believe the following can be an intuitive and beautiful solution (I used desc_ as column name rather than desc which is a reserved word):
select (select desc_ from ErrorCodes x where x.type_code = a.typeCode) desc_
from ErrorXML a
where (select desc_ from ErrorCodes x where x.type_code = a.typeCode) is not null
order by row_index
limit 1;
If you also need to handle the case if query returns no row then for MySQL, following syntax should suffice. For other databases you can use similar encapsulation with isnull, nvl, etc:
select ifnull((select (select desc_ from ErrorCodes x where x.type_code = a.typeCode) desc_ from ErrorXML a where (select desc_ from ErrorCodes x where x.type_code = a.typeCode) is not null order by row_index limit 1), 'UNKNOWN');
To test I used following scripts and seems to work properly:
create database if not exists stackoverflow;
use stackoverflow;
drop table if exists ErrorCodes;
create table ErrorCodes
(
type_code varchar(2),
desc_ varchar(10)
);
insert into ErrorCodes(type_code, desc_) values
('01', 'Error101'),
('02', 'Error99'),
('03', 'Error120');
drop table if exists ErrorXML;
create table ErrorXML
(
row_index integer,
typeCode varchar(2)
);
insert into ErrorXML(row_index, typeCode) values
('1', '87'),
('2', '02'),
('3', '01');
Final-1 quote: While generating your tables try to use same column names as much as possible. I.e. I'd suggest ErrorXML to use type_code rather than typeCode.
Final quote: I choose to use lower letters in SQL since capital letters should be used while emphasizing an important point. I also suggest that style.
What about this: Do a subquery to bring back the first row_index for each type_code.
Do a LEFT OUTER Join on the ErrorCodes table so that you get NULLs as well.
SELECT
ISNULL(ErrorCodes.desc,'unknown') AS description
ErrorXML.row_index
FROM ErrorCodes
LEFT OUTER JOIN (
SELECT type_code, MIN(row_index) AS row_index
FROM ErrorXML
GROUP BY type_code
) AS ErrorXML ON ErrorCodes.type_code = ErrorXML .type_code

Get values from 2 tables based on ID

Consider below tables
Job Table
JobID AnswerID UserID
1 1,2 1
2 2,3 2
3 1,3 3
Answer Table
AnswerID Answer QuestionID
1 Clean 1
2 Install 1
3 Other 2
For this I need to get the result as below
JobID Answer UserID
1 Clean,Install 1
2 Install,Other 2
3 Clean,Other 3
Please help to write MSSQL query for this.
You are storing a list of ids as a comma separated list. This is a really bad idea for several reasons:
Storing numbers as strings is a bad idea.
You cannot define foreign key relationships.
SQL does not have great support for strings.
Any attempt to join to the original table will be inefficient, because of the type conversion.
Such a structure violates the idea that a column contains a single value.
There is a proper way to store lists in a relational database. It is called a "table". You want a junction table with one row per job and answer. I would call it JobAnswers.
With the proper data structure, your query would be trivial.
Although I agree with Gordon Linoff I do understand we have no control over what we inherit from previous developers.
here is what you require to do:
Sample data
CREATE TABLE #temp
(
JobID INT, AnswerID VARCHAR(10), UserID INT
);
INSERT INTO #temp
VALUES
(1, '1,2', 1
),
(2, '2,3', 2
),
(3, '1,3', 3
);
CREATE TABLE #temp2
(
AnswerID INT, Answer VARCHAR(10), QuestionID INT
);
INSERT INTO #temp2
VALUES
(1, 'Clean', 1
),
(2, 'Install', 1
),
(3, 'Other', 2
);
Query:
SELECT #temp.JobID,
(
SELECT #temp2.Answer
FROM #temp2
WHERE #temp2.AnswerID = SUBSTRING(#temp.AnswerID, 1, CHARINDEX(',', #temp.AnswerID)-1)
)+','+
(
SELECT #temp2.Answer
FROM #temp2
WHERE #temp2.AnswerID = SUBSTRING(#temp.AnswerID, CHARINDEX(',', #temp.AnswerID)+1, LEN(#temp.AnswerID)-1)
) AS Answer,
#temp.UserID
FROM #temp;
Result:
You can try to use subqueries like this:
SELECT job.jobID,
(SELECT answer.answer FROM answer WHERE answer.answerID IN (job.answerID)) as answers,
job.userID

Is there any way to get postgresql to report results from a join?

In other statistical softwares (STATA), when you perform a join between two separate tables there are options to reports the results of a join
For instance, if you join a table with another table on a column and the second table has non-unique values, it reports that.
Likewise, if you perform an inner join it reports the number of rows dropped from both tables and if you perform a left or right outer join it lets you know how many rows were unmatched.
It will need a nasty outer join. Here is the CTE version:
-- Some data
CREATE TABLE bob
( ID INTEGER NOT NULL
, zname varchar
);
INSERT INTO bob(id, zname) VALUES
(2, 'Alice') ,(3, 'Charly')
,(4,'David') ,(5, 'Edsger') ,(6, 'Fanny')
;
CREATE TABLE john
( ID INTEGER NOT NULL
, zname varchar
);
INSERT INTO john(id, zname) VALUES
(4,'David') ,(5, 'Edsger') ,(6, 'Fanny')
,(7,'Gerard') ,(8, 'Hendrik') ,(9, 'Irene'), (10, 'Joop')
;
--
-- Encode presence in bob as 1, presence in John AS 2, both=3
--
WITH flags AS (
WITH b AS (
SELECT 1::integer AS flag, id
FROM bob
)
, j AS (
SELECT 2::integer AS flag, id
FROM john
)
SELECT COALESCE(b.flag, 0) + COALESCE(j.flag, 0) AS flag
FROM b
FULL OUTER JOIN j ON b.id = j.id
)
SELECT flag, COUNT(*)
FROM flags
GROUP BY flag;
The result:
CREATE TABLE
INSERT 0 5
CREATE TABLE
INSERT 0 7
flag | count
------+-------
1 | 2
3 | 3
2 | 4
(3 rows)
As far as I know there is no option to do that within Postgres, although you could get a guess by looking at the estimates.
Calculating the missing rows requires you to count all rows so databases generally try to avoid things like that.
The options I can think of:
writing multiple queries
doing a full outer join and filtering the results (maybe with a subquery... can't think of a good way which will always easily work)
use writable complex table expressions to log the intermediate results

Postgresql aggregate array

I have a two tables
Student
--------
Id Name
1 John
2 David
3 Will
Grade
---------
Student_id Mark
1 A
2 B
2 B+
3 C
3 A
Is it possible to make native Postgresql SELECT to get results like below:
Name Array of marks
-----------------------
'John', {'A'}
'David', {'B','B+'}
'Will', {'C','A'}
But not like below
Name Mark
----------------
'John', 'A'
'David', 'B'
'David', 'B+'
'Will', 'C'
'Will', 'A'
Use array_agg: http://www.sqlfiddle.com/#!1/5099e/1
SELECT s.name, array_agg(g.Mark) as marks
FROM student s
LEFT JOIN Grade g ON g.Student_id = s.Id
GROUP BY s.Id
By the way, if you are using Postgres 9.1, you don't need to repeat the columns on SELECT to GROUP BY, e.g. you don't need to repeat the student name on GROUP BY. You can merely GROUP BY on primary key. If you remove the primary key on student, you need to repeat the student name on GROUP BY.
CREATE TABLE grade
(Student_id int, Mark varchar(2));
INSERT INTO grade
(Student_id, Mark)
VALUES
(1, 'A'),
(2, 'B'),
(2, 'B+'),
(3, 'C'),
(3, 'A');
CREATE TABLE student
(Id int primary key, Name varchar(5));
INSERT INTO student
(Id, Name)
VALUES
(1, 'John'),
(2, 'David'),
(3, 'Will');
What I understand you can do something like this:
SELECT p.p_name,
STRING_AGG(Grade.Mark, ',' ORDER BY Grade.Mark) As marks
FROM Student
LEFT JOIN Grade ON Grade.Student_id = Student.Id
GROUP BY Student.Name;
EDIT
I am not sure. But maybe something like this then:
SELECT p.p_name, 
    array_to_string(ARRAY_AGG(Grade.Mark),';') As marks
FROM Student
LEFT JOIN Grade ON Grade.Student_id = Student.Id
GROUP BY Student.Name;
Reference here
You could use the following:
SELECT Student.Name as Name,
(SELECT array(SELECT Mark FROM Grade WHERE Grade.Student_id = Student.Id))
AS ArrayOfMarks
FROM Student
As described here: http://www.mkyong.com/database/convert-subquery-result-to-array/
Michael Buen got it right. I got what I needed using array_agg.
Here just a basic query example in case it helps someone:
SELECT directory, ARRAY_AGG(file_name)
FROM table
WHERE type = 'ZIP'
GROUP BY directory;
And the result was something like:
| parent_directory | array_agg |
+-------------------------+----------------------------------------+
| /home/postgresql/files | {zip_1.zip,zip_2.zip,zip_3.zip} |
| /home/postgresql/files2 | {file1.zip,file2.zip} |
This post also helped me a lot: "Group By" in SQL and Python Pandas.
It basically says that it is more convenient to use only PSQL when possible, but that Python Pandas can be useful to achieve extra functionalities in the filtering process.