SQL blank rows between rows - sql

I am trying to output a blank row after each row.
For example:
SELECT id,job,amount FROM table
+----+-----+--------+
| id | job | amount |
+----+-----+--------+
| 1 | 100 | 123 |
| 2 | 200 | 321 |
| 3 | 300 | 421 |
+----+-----+--------+
To the following:
+----+-----+--------+
| id | job | amount |
+----+-----+--------+
| 1 | 100 | 123 |
| | | |
| 2 | 200 | 321 |
| | | |
| 3 | 300 | 421 |
+----+-----+--------+
I know I can do similar things with a UNION like:
SELECT null AS id, null AS job, null AS amount
UNION
SELECT id,job,amount FROM table
Which would give me a blank row at the beginning, but for the life of me I can't figure out how to do it every second row. A nested SELECT/UNION? - Have tried but nothing seemed to work.
The DBMS is SQL Server 2016

This is an akward requirement, that would most probably better handled on application side. Here is, however, one way to do it:
select id, job, amount
from (
select id, job, amount, id order_by from mytable
union all
select null, null, null, id from mytable
) t
order by order_by, id desc
The trick is to add an additional column to the unioned query, that keeps track of the original id, and can be used to sort the records in the outer query. You can then use id desc as second sorting criteria, which will put null values in second position.
Demo on DB Fiddle:
with mytable as (
select 1 id, 100 job, 123 amount
union all select 2, 200, 321
union all select 3, 300, 421
)
select id, job, amount
from (
select id, job, amount, id order_by from mytable
union all
select null, null, null, id from mytable
) t
order by order_by, id desc;
id | job | amount
---: | ---: | -----:
1 | 100 | 123
null | null | null
2 | 200 | 321
null | null | null
3 | 300 | 421
null | null | null

In SQL Server, you can just use apply:
select v.id, v.job, v.amount
from t cross apply
(values (id, job, amount, id, 1),
(null, null, null, id, 2)
) v(id, job, amount, ord1, ord2)
order by ord1, ord2;

Related

SQL: SELECT all and Remove duplicated rows based on values from multiple columns

I have the following SQL table name 'orders':
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
| 111 | 1234 | 23 | 1 |
+--------------+------------+-------------------+------------+
And I'm trying to Select only the rows that the order_id and item_id are NOT the same (remove duplicates only if BOTH has the same value), I tried using "Group_By" as follows:
SELECT * FROM orders GROUP BY order_id,item_id
but this, will remove all duplicates of order_id, and all duplicated of item_id, here is the result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
+--------------+------------+-------------------+------------+
I've tried using 'DISTINCT' too, but I need to select all columns in the result.
here is the expected result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
+--------------+------------+-------------------+------------+
I hope its clear, Thank you.
You can use row_number():
select order_id, item_id, amount, commission
from (
select t.*, row_number() over(partition by order_id, item_id order by commission) rn
from mytable t
) t
where rn = 1
With your sample data, it is not easy to see exactly which partition and order by clause you are looking for, so you might need to adjust them to your exact use case.
Simply:
SELECT DISTINCT * FROM orders
SELECT DISTINCT should do what you want
SELECT DISTINCT order_id, item_id, amount, commission
FROM orders;
If you have more columns, but only care about these being duplicated, then you can use ROW_NUMBER():
SELECT o.*
FROM (SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY order_id, item_id, amount, commission ORDER BY (SELECT NULL)) as seqnum
FROM orders o
) o
WHERE seqnum = 1;
You can try this.
create table MyTable
(order_id int
, item_id int
, amount int
, commission int)
insert into MyTable values
(111, 1234, 23, 1),
(222, 1234, 34, 2),
(111, 2345, 45, 3),
(111, 1234, 23, 1)
select distinct * from MyTable
Live db<>fiddle demo.

Select multiple rows for distinct column if column not null, otherwise select first row where column is null

I have an interesting query I need to execute. For Table A below, I want to select ALL non null phoneNumber for distinct userId, but if a non null phoneNumber value doesn't exist for distinct userId select only one null phoneNumber for distinct userId.
Table A
| id | userId | phoneNumber | emailAddress |
-------------------------------------------
| 1 | 1 | 0123456789 | null |
| 2 | 1 | 1234567890 | null |
| 3 | 1 | null | test#gmail |
| 4 | 2 | null | andy#yahoo |
| 5 | 2 | null | andy#gmail |
Expected Results
| id | userId | phoneNumber | emailAddress |
-------------------------------------------
| 1 | 1 | 0123456789 | null |
| 2 | 1 | 1234567890 | null |
| 5 | 2 | null | andy#gmail |
I wrote the query below and it returns the desired results, but I'm interested to see if there is a better, more optimal way to achieve this. Rather than writing multiple subqueries.
SELECT *
FROM A
WHERE phoneNumber IS NOT NULL
UNION
SELECT *
FROM A
WHERE id IN (SELECT MAX(id)
FROM A WHERE phoneNumber IS NULL
AND userId NOT IN (SELECT userId
FROM A
WHERE phoneNumber IS NOT NULL)
GROUP BY userId)
You can use the COUNT() and ROW_NUMBER() analytic functions:
SELECT *
FROM (
SELECT A.*,
COUNT( phoneNumber) OVER ( PARTITION BY userId ) AS ct,
ROW_NUMBER() OVER ( PARTITION BY userId ORDER BY id DESC ) AS rn
FROM A
)
WHERE phoneNumber IS NOT NULL
OR ( ct = 0 AND rn = 1 );

Only return top n results for each group in GROUPING SETS query

I have a rather complicated query performing some aggregations using GROUPING SETS, it looks roughly like the following:
SELECT
column1,
[... more columns here]
count(*)
FROM table_a
GROUP BY GROUPING SETS (
column1,
[... more columns here]
)
ORDER BY count DESC
This works very well in general, as long as the number of results for each group is reasonably small. But I have some columns in this query that can have a large number of distinct values, which results in a large amount of rows returned by this query.
I'm actually only interested in the top results for each group in the grouping set. But there doesn't seem to be an obvious way to limit the number of results per group in a query using grouping sets, LIMIT doesn't work in this case.
I'm using PostgreSQL 9.6, so I'm not restricted in which newer features I can use here.
So what my query does is something like this:
| column1 | column2 | count |
|---------|---------|-------|
| DE | | 32455 |
| US | | 3445 |
| FR | | 556 |
| GB | | 456 |
| RU | | 76 |
| | 12 | 10234 |
| | 64 | 9805 |
| | 2 | 6043 |
| | 98 | 2356 |
| | 65 | 1023 |
| | 34 | 501 |
What I actually want is something that only returns the top 3 results:
| column1 | column2 | count |
|---------|---------|-------|
| DE | | 32455 |
| US | | 3445 |
| FR | | 556 |
| | 12 | 10234 |
| | 64 | 9805 |
| | 2 | 6043 |
Use row_number and grouping
select a, b, total
from (
select
a, b, total,
row_number() over(
partition by g
order by total desc
) as rn
from (
select a, b, count(*) as total, grouping ((a),(b)) as g
from t
group by grouping sets ((a),(b))
) s
) s
where rn <= 3
Something like this:
WITH T(column1 , column2, cnt) AS
(
SELECT 'kla', 'k', 10
UNION ALL
SELECT 'kle', 'm', 30
UNION ALL
SELECT 'foo', 'k', 10
UNION ALL
SELECT 'bar', 'm', 30
UNION ALL
SELECT 'bar', 'k', 20
UNION ALL
SELECT 'foo', 'm', 15
UNION ALL
SELECT 'foo', 'p', 10
),
tt AS (select column1, column2, COUNT(*) AS cnt from t GROUP BY GROUPING SETS( (column1), (column2)) )
(SELECT column1, NULL as column2, cnt FROM tt WHERE column1 IS NOT NULL ORDER BY cnt desc LIMIT 3)
UNION ALL
(SELECT NULL as column1, column2, cnt FROM tt WHERE column2 IS NOT NULL ORDER BY cnt desc LIMIT 3)

How to duplicate records in same table

I have been stuck for a good while on this issue now and have made zero progress. I don't even know if it is possible...
I have 1 table:
+------+------------+-------+---------+------------+
| Item | Date | RUnit | FDHUnit | Difference |
+------+------------+-------+---------+------------+
| A | 19/04/2016 | 21000 | 20000 | 1000 |
| B | 20/04/2016 | 2500 | 500 | 2000 |
+------+------------+-------+---------+------------+
Is it possible to Create a new row in the same table for each of those items which will display the Difference and perhaps a few other columns?
My desired output would be something like this:
+------+------------+-------+---------+------------+
| Item | Date | RUnit | FDHUnit | Difference |
+------+------------+-------+---------+------------+
| A | 19/04/2016 | 21000 | 20000 | |
| A | 19/04/2016 | NULL | NULL | 1000 |
| B | 20/04/2016 | 2500 | 500 | |
| B | 20/04/2016 | NULL | NULL | 2000 |
+------+------------+-------+---------+------------+
Reason being is that i would like to show a new column and indicate that it is either Held directly or not held directly.
Yes, use union all:
select item, date, ruunit, fdhunit, difference
from t
union all
select item, date, null, null, runit - fdhunit
from t
order by item, (case when runit is not null then 1 else 2 end);
The order by puts the results in the order that your results suggest. Without an order by, the ordering of the records is indeterminate.
try this way
select * from
(select item, date, ruunit, fdhunit, '' as difference
from t
union all
select item, date, null as ruunit, null as fdhunit, difference
from t) a
order by item, date
Try This
Insert into table :
insert into table1
select item,date,null,null,(Runit-fdhunit) from table1 where (Runit-fdhunit)
Normal result :
select * from table1
union all
select item,date,null,null,(Runit-fdhunit) from table1 where (Runit-fdhunit) <>0
Try this. Replace cast(null as number) with actual type of the difference column
select item, date, r.ruunit, r.fdhunit, r.difference
from t
cross apply (
select n=1, ruunit, fdhunit, cast(null as number) difference
UNION
select n=2, null, null, difference ) r
order by item, date, n

Querying data groups with total row before starting next group?

I need to query some data in the below format in SQL Server:
Id Group Price
1 A 10
2 A 20
Sum 30
1 B 6
2 B 4
Sum 10
1 C 100
2 C 200
Sum 300
I was thinking to do it in the follwoing steps:
Query one group
In other query do sum
Use Union operator to combine this result set
Do step 1-3 for all groups and finally return all sub sets of data using union.
Is there a better way to do this ? May be using some out of box feature ? Please advise.
Edit:
As per suggestions and code sample I tried this code:
Select
Case
when id is null then 'SUM'
else CAST(id as Varchar(10)) end as ID,
Case when [group] is null then 'ALL' else CAST([group] as Varchar(50)) end as [group]
,Price from
(
SELECT Id, [Group],BGAApplicationID,Section, SUM(PrimaryTotalArea) AS price
FROM vwFacilityDetails
where bgaapplicationid=1102
GROUP BY Id, [Group],BGAApplicationID,Section WITH ROLLUP
) a
And Even this code as well:
Select Id, [Group],BGAApplicationID,Section, SUM(PrimaryTotalArea) AS price
From vwFacilityDetails
Where Not ([group] Is Null And id Is Null And BGAApplicationId is null and section is null) and BGAApplicationId=1102
Group By Id, [Group],BGAApplicationID,Section
With Rollup
In results it groups up the data but for every record it shows it 3 times (in both above codes) like:
2879 Existing Facilities Whole School 25.00
2879 Existing Facilities Whole School 25.00
2879 Existing Facilities Whole School 25.00
2879 ALL 25.00
I guess there is some issue in my query, please guide me here as well.
Thanks
SQL Server introduced GROUPING SETS which is what you should be looking to use.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
Create Table vwFacilityDetails (
id int not null,
[group] char(1) not null,
PrimaryTotalArea int not null,
Section int,
bgaapplicationid int
);
Insert Into vwFacilityDetails (id, [group], Section,bgaapplicationid,PrimaryTotalArea) values
(1, 'A', 1,1102,2),
(1, 'A', 1,1102,1),
(1, 'A', 1,1102,7),
(2, 'A', 1,1102,20),
(1, 'B', 1,1102,6),
(2, 'B', 1,1102,4),
(1, 'C', 1,1102,100),
(2, 'C', 1,1102,200);
Query 1:
SELECT CASE WHEN Id is null then 'SUM'
ELSE Right(Id,10) end Id,
[Group],BGAApplicationID,Section,
SUM(PrimaryTotalArea) price
FROM vwFacilityDetails
where bgaapplicationid=1102
GROUP BY GROUPING SETS (
(Id,[Group],BGAApplicationID,Section),
([Group])
)
ORDER BY [GROUP],
ID;
Results:
| ID | GROUP | BGAAPPLICATIONID | SECTION | PRICE |
----------------------------------------------------
| 1 | A | 1102 | 1 | 10 |
| 2 | A | 1102 | 1 | 20 |
| SUM | A | (null) | (null) | 30 |
| 1 | B | 1102 | 1 | 6 |
| 2 | B | 1102 | 1 | 4 |
| SUM | B | (null) | (null) | 10 |
| 1 | C | 1102 | 1 | 100 |
| 2 | C | 1102 | 1 | 200 |
| SUM | C | (null) | (null) | 300 |
Select
id,
[Group],
SUM(price) AS price
From
Test
Group By
[group],
id
With
Rollup
http://sqlfiddle.com/#!3/080cd/8
Select Case when id is null then 'SUM' else CAST(id as Varchar(10)) end as ID
,Case when [group] is null then 'ALL' else CAST([group] as Varchar(10)) end as [group]
,Price from
(
SELECT id, [group], SUM(price) AS Price
FROM IG
GROUP BY [GROUP],ID WITH ROLLUP
) a