Order by multiple columns with - mysql

I'm trying to sort a table by multiple columns, but I should shift the most important column.
Given a sample like:
Name Col1 Col2 Col3
Paul 1 2 1
John 1 1 1
Greg NULL 2 1
Jane 2 2 1
I would like the data to be sorted like John, Paul, Greg and Jane.
If I do ORDER BY Col1, Col2, Col3, Greg is the first result because of NULL.
What I'm trying to do is to "shift" the columns, to discard all the NULL values
Edited:
The desired result is
John
Paul
Greg
Jane
Greg ends up third because the query should remove the null value, working on a "fixed" version like
Name Col1 Col2 Col3
Paul 1 2 1
John 1 1 1
Greg 2 1 - <== Greg cols moved to the left by one step
Jane 2 2 1
Edit2:
I think that something like the COALESCE function should help.
If I run:
SELECT Name, COALESCE(Col1, Col2, Col3, 0) FROM Table1
The result is:
Paul 1
John 1
Greg 2
Jane 2

You Need to derive a new column as per the required order using case statement and use that column to sort the result like below.
select Name,col1,col2,col3
from(
Select *,case when Name='John' then 1
when Name='paul' then 2
when Name= 'Greg' then 3
when Name = 'Jane' then 4
else 5
end as sortid
from test
) a
order by sortid, Name

Use CASE expressions in the ORDER BY clause to check whether a column is null and needs to be shifted:
SELECT *
FROM tablename
ORDER BY COALESCE(Col1, Col2, Col3),
CASE
WHEN Col1 IS NOT NULL THEN COALESCE(Col2, Col3)
WHEN Col2 IS NOT NULL THEN Col3
END,
CASE WHEN Col1 IS NOT NULL AND Col2 IS NOT NULL THEN Col3 END;
See the demo.

Related

GROUP BY clause ignoring first column value if second and third are the same

Database server is MySQL
I have an SQL query that does a select with a "GROUP BY" clause as follows:
SELECT col1, col2, col3, SUM(col4), SUM(col5) where col6 = 20 GROUP BY col1, col2, col3
All works well when col2 and col3 have different values but when col2 and col3 have the same value as follows
col1 col2 col3 col4 col5
a 1 2 1 1
b 1 2 2 2
b 1 2 3 3
c 1 2 4 4
the query result is
c 1 2 10 10
I was expecting
a 1 2 1 1
b 1 2 5 5
c 1 2 4 4
Why does the "GROUP BY" ignore the fact that the col1 values are not all the same and just displays that last one it finds?
Thanks in advance
User error - turn out I was aliasing col1 and there was a column of the same name in the table so the GROUP BY was using the column name and not the alias. The column name is question was not even part of the query.
The pitfalls of working on a system that someone else wrote.
Thanks for all your suggestions/comments

What is the best way for selecting 2 row from table in one row?

I have a table like following
TABLE_A
ID PERSON_ID NAME GRADE
---------- ---------- ---------- ----------
1 1 NAME_1 10
2 1 NAME_1 20
3 2 NAME_2 30
4 2 NAME_2 40
...
in this table, for each name there is exactly two rows (two grades).
I want to make a query which results like following
RESULT
PERSON_ID NAME GRADE1 GRADE_2
---------- ---------- ---------- ----------
1 NAME_1 10 20
2 NAME_2 30 40
What is the best way for this.
I can use self join but I think this is not correct method
You can use GROUP BY as the other person suggested.
Or you can make a join.
select t1.person_id, t1.grade as grade1, t2.grade as grade2
from TABLE_A t1 join TABLE_A t2 on t1.person_id=t2.person_id and t1.id!=t2.id
This JOIN joins all the rows with the same person, but not the rows with the same id so you filter out the duplicates.
If you are using MySQL, and if all names have exactly two records, and the first grade is the one with the lower id and the second grade the other, then you could use a query like this one:
select
person_id,
name,
group_concat(grade order by id) as grades
from
table_a
group by
person_id,
name
then if you want to separate grades into two new columns you could use SUBSTRING_INDEX:
select
person_id,
name,
substring_index(group_concat(grade order by id), ',', 1) as grade1,
substring_index(group_concat(grade order by id), ',', -1) as grade2
from
table_a
group by
person_id,
name
Try this for SQL SERVER
; WITH CTE AS(
SELECT PERSON_ID, NAME, GRADE AS GRADE_1,
LEAD(GRADE) OVER(PARTITION BY NAME ORDER BY NAME) AS GRADE_2
FROM TABLE_A
)
SELECT * FROM CTE
WHERE GRADE_2 IS NOT NULL
RESULT SET:
PERSON_ID NAME GRADE_1 GRADE_2
---------- ---------- ---------- ----------
1 NAME_1 10 20
2 NAME_2 30 40
As soon as the topic starter didn't mention the ORDER of grades and "there is exactly two rows (two grades)" I think the easiest way:
SELECT PERSON_ID,
MAX(NAME) as NAME,
MIN(GRADE) as GRADE1,
MAX(GRADE) as GRADE2
FROM TABLE_A
GROUP BY PERSON_ID
I found best answer for PostgreSQL in this link
https://www.postgresql.org/docs/9.2/static/tablefunc.html
In PostgreSQL there is the crosstab(text) function.
The crosstab function is used to produce "pivot" displays, wherein data is listed across the page rather than down. For example, we might have data like
row1 val11
row1 val12
row1 val13
...
row2 val21
row2 val22
row2 val23
...
which we wish to display like
row1 val11 val12 val13 ...
row2 val21 val22 val23 ...
...
The crosstab function takes a text parameter that is a SQL query producing raw data formatted in the first way, and produces a table formatted in the second way.
The sql parameter is a SQL statement that produces the source set of data.
This statement must return one row_name column, one category column, and one value column. N is an obsolete parameter, ignored if supplied (formerly this had to match the number of output value columns, but now that is determined by the calling query).
For example, the provided query might produce a set something like:
row_name cat value
----------+-------+-------
row1 cat1 val1
row1 cat2 val2
row1 cat3 val3
row1 cat4 val4
row2 cat1 val5
row2 cat2 val6
row2 cat3 val7
row2 cat4 val8
The crosstab function is declared to return setof record, so the actual names and types of the output columns must be defined in the FROM clause of the calling SELECT statement, for example:
SELECT * FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);
This example produces a set something like:
<== value columns ==>
row_name category_1 category_2
----------+------------+------------
row1 val1 val2
row2 val5 val6
For more read section F.35.1.2. in URL entered top of the answer

How to split one row into two based on 2 different column values

I am trying to accomplish something simple, but cant get to think straight. I have a case where 1 row can have different values in 2 different columns. But if thats the case then instead of displaying just 1 row for these 2 values, I need to display 2 rows for 1 column value each..for example.
ID Col1 col2 col3 col4
46054 2011W3974 164505 1 2
58765 2014W3777 275908 1 NULL
52311 2013W1877 247047 1 NULL
63032 2015W3317 295279 1 NULL
57552 2014W2813 274810 1 NULL
44584 2011W2622 173985 1 2
This needs to be split into 2 rows for row 1 and 6 into 2 rows like below:
46054 2011W3974 164505 1 NULL
46054 2011W3974 164505 NULL 2
58765 2014W3777 275908 1 NULL
52311 2013W1877 247047 1 NULL
63032 2015W3317 295279 1 NULL
57552 2014W2813 274810 1 NULL
44584 2011W2622 173985 1 NULL
44584 2011W2622 173985 NULL 2
What is the best possible way to do this. I looked at SPLIT XML function, but I dont think that will be helpful here. I also played with ranking functions, but since this is 2 columns, I dont think that will work either. Please suggest
Thanks,
RV
I'd properly just union it together:
SELECT Id, Col1, Col2, Col3, NULL AS Col4
FROM <Your Table>
WHERE col4 is NULL
UNION
SELECT Id, Col1, Col2, NULL, Col4
FROM <Your Table>
WHERE Col4 = 2
Just use Union not union All.
SELECT Id, Col1, Col2, Col3, NULL AS Col4
FROM YourTable
WHERE isnull(Col4 , 0) = 0
UNION
SELECT Id, Col1, Col2, NULL as Col3, Col4
FROM YourTable
WHERE isnull(Col3 , 0) = 0

how to achieve concatenation using group by

Suppose there is a table named 'a' with following data:
col1, col2
-----------
1 1
1 2
1 3
2 2
2 3
3 4
then to achieve following results:
col1, col2
--------------
1 6
2 5
3 4
i can run query like :
select col1, sum(col2) from a group by col1.
But suppose my table is:
col1, col2
---------
1 a
1 b
1 c
2 d
2 e
3 f
here col2 is of varchar type not of numeric type.
what will be the sql query to give following results???
col1, col2
------------
1 a,b,c
2 d,e
3 f
i have tried group by on col1 but how to concatenate values in col2???
the problem is that col2 is of varchar type.
In case of MySQL you can use GROUP_CONCAT like this:
SELECT
col1,
GROUP_CONCAT(col2) as col2
FROM demo
GROUP BY col1;
Here is the sqlfiddle.
In case of SQL Server you can use STUFF like this:
SELECT t1.col1,
stuff((SELECT ',' + CAST(t2.col2 as VARCHAR(10))
FROM demo t2 WHERE t1.col1 = t2.col1
FOR xml path('')),1,1,'') col2
FROM demo t1
GROUP BY t1.col1;
Here is the sqlfiddle.
You can use group_concat function in mysql
select
col1,
group_concat(col2) as col2
from table_name
group by col1
Here is a good example, I ran into a similar issue whilst coding up a schedule (working example: www.oldiesplus.com/schedule/)
Here is the link to my question with answer: https://stackoverflow.com/a/27047139

left join not working - get records not in other table

I feel weird, as every other answer says it works, but I can't get the correct results :(
table A: id_num, name<br>
table B: id_num
table A has index on name, but not unique. id_num is unique in this table.<br>
table B has index on id_num, but not unique.
I want to get table A names, who are NOT in table B.
This not working:
**SELECT a.name FROM a
LEFT JOIN b ON (a.id_num = b.id_num)
WHERE b.id_numb IS NULL**
its returning names that ARE in table b (and some that are not).
this didn't work either:
**SELECT distinct(a.name)
FROM a where a.id_num
not in (select distinct(b.id_num) from b )**
I can't comprehend how a.names are being return who ARE IN table B, when the SQL says NOT IN.
what am I missing?
thanks
By right, the left join should work. And as well as in.
Here is a sample. So you may want to publish and show your table schema and data for more justice on your question. Best if you could just create the tables on SQLFIDDLE and show the reference. ;)
Here is just SAMPLE:-
SQLFIDDLE reference
Sample Tables:
tab1:
COL1 COL2
1 john
2 tim
3 jack
4 rose
tab2:
COL3 COL4
1 2
2 3
3 5
4 5
5 2
Query:
select * from tab1
where col1 not in
(select distinct col4 from tab2)
;
Results:
COL1 COL2
1 john
4 rose
As per OP's updated comments and table structure
OP mentioned the table tab1 will have multiple records for same name. According to his original table design, there's NO NAME field in the table tab2. It's also much recommended if OP had provided the expected results initially.
* SQLFIDDLE Reference
OP's table data:
COL1 COL2
1 john
2 tim
3 jack
4 rose
5 john
6 john
COL3 COL4
1 2
2 3
3 5
4 5
5 2
6 6
Query: When you do not want any name that is duplicated
select t.col1, t.col2
from tab1 t
join
(select t1.col2, count(*) as counts
from tab1 t1
group by t1.col2
having count(*) = 1) x
on t.col2 = x.col2
where t.col1 not in
(select distinct col4 from tab2)
;
Results: Here is Rose, the only record that has no duplicates and do not exist in tab2
COL1 COL2
4 rose
try this
select t1.name from table1 t1
where t1.name not in
(select t2.name from table2 t2 )
look the DEMO SQLFIDDLE
EDIT:
if you have only id_num in your second table then here look to
THIS DEMO SQLFIDDLE