SQL. Is it possible? - sql

Oracle
select * from table1;
column1 | column2 | column3 |
a | 2010 | 1 |
a | 2011 | 2 |
a | 2012 | 3 |
b | 2010 | 4 |
b | 2011 | 5 |
b | 2012 | 6 |
c | 2010 | 7 |
c | 2011 | 8 |
c | 2012 | 9 |
Is it possible to do something like this.
column1 | 2010 | 2011 | 2012 |
a | 1 | 2 | 3 |
b | 4 | 5 | 6 |
c | 7 | 8 | 9 |

Yes
SELECT t.column1,
(SELECT SUM(column3) FROM table1
WHERE column1 = t.column1 AND column2 = 2010) AS "2010",
(SELECT SUM(column3) FROM table1
WHERE column1 = t.column1 AND column2 = 2011) AS "2011",
(SELECT SUM(column3) FROM table1
WHERE column1 = t.column1 AND column2 = 2012) AS "2012"
FROM (
SELECT DISTINCT column1 FROM table1
) t
ORDER BY t.column1
Note, I've added the SUM() aggregate function around colum3 in case you may have duplicate values per column1, column2.
Depending on the database you're using, the following equivalent query might be a bit faster:
SELECT t.column1,
(SELECT SUM(column3) FROM table1
WHERE column1 = t.column1 AND column2 = 2010) AS "2010",
(SELECT SUM(column3) FROM table1
WHERE column1 = t.column1 AND column2 = 2011) AS "2011",
(SELECT SUM(column3) FROM table1
WHERE column1 = t.column1 AND column2 = 2012) AS "2012"
FROM table1 t
GROUP BY t.column1
ORDER BY t.column1
Note that you can achieve the same in a more concise way, using the PIVOT clause (as others have suggested). In Oracle 11g, this would translate to:
SELECT column1, "2010", "2011", "2012"
FROM table1
PIVOT (SUM(column3) FOR column2 IN (2010, 2011, 2012))
In any case, I don't know any database that allows for a dynamic number of columns per table expression, without resorting to tricks involving XML or other means of dynamic SQL. Typically, those tricks aren't much faster than what I suggested here. This means, you'll always have to foresee, how many years you want to support as columns, and adapt your query accordingly

try PIVOT in SQL sever
select column1 , [2010],[2011],[2012]
from your_table
PIVOT (MAX(column3) FOR column2 IN ([2010],[2011],[2012])) P
Edit1:
If your Column2 is dynamic, then try this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(column2)
from your_table
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT column1 , ' + #cols + ' from your_table
pivot
(
MAX(column3)
for column2 in (' + #cols + ')
) p '
print(#query)
execute(#query)

Related

SQL Partition By Function without aggregation

I have a table with data like the following:
Column1 | Column2 | Column3 | Value
SQ03 | D | 1000040 | 1000
SQ03 | | 1000040 | 1000
SQ03 | | 1000050 | 2000
SQ03 | | 1000060 | 3000
SQ03 | L | 1000060 | 3000
SQ03 | D | 1000060 | 3000
What I need to do is to get a single value based on column3. Is a value in column3 is unique, I need to get that value. But if there are duplicates in Column3, I need to get the value where Column2 is not null. But like in the example that I showed in above, there are values for Column3 where Column2 is marked more than once, in these cases I need to get only one of these values, doesn't matter what.
So I thought on flagging which line I would need with the following solution:
select *, CASE
WHEN "Column2" != ' '
THEN 'X'
WHEN "Column2" = ' ' AND row_number() over(PARTITION BY "Column3" ORDER BY "Column2" DESC, "Column3") = 1
THEN 'X'
ELSE 'O'
END AS "FLAG" from DUMMY
WHERE "Column1" = 'SQ03'
But the problem with this solution is that it's aggregating the value from Column3. Like, it sums the values where Column3 has duplicates.
Can anyone help me with a solution where I don't get the values aggregated?
EDIT:
My expected output would be this:
Column1 | Column2 | Column3 | Value
SQ03 | D | 1000040 | 1000
SQ03 | | 1000050 | 2000
SQ03 | L | 1000060 | 3000
You can use a subquery to generate row numbers for each Column3 value (ordered by Column2 DESC to make NULL values come last), and then select the rows which have row_number = 1:
SELECT Column1, Column2, Column3, Value
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Column3 ORDER BY Column2 DESC) AS rn
FROM DUMMY
WHERE Column1 = 'SQ03'
) D
WHERE rn = 1
Alternatively you can use a CTE:
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Column3 ORDER BY Column2 DESC) AS rn
FROM DUMMY
WHERE Column1 = 'SQ03'
)
SELECT Column1, Column2, Column3, Value
FROM CTE
WHERE rn = 1
Output for both queries:
Column1 Column2 Column3 Value
SQ03 D 1000040 1000
SQ03 (null) 1000050 2000
SQ03 L 1000060 3000
Demo on SQLFiddle
I think an aggregation function (as a window function) does what you want:
select t.*,
max(column3) over (partition by column1)
from t;

SQL Server - updating distinct values

I have 3 columns of data:
Column1 has duplicate values eg a a b b c c
Column2 has all NULL values
Column3 has other data that is not really important
I want to update Column2 with a value eh Hello but only for 1 instance of each value for column1. Eg, a = Hello but the 2nd instance of Hello is NULL, same with b c and so on.
I can find the distinct value by using this:
select distinct Column1
from TABLENAME
But when I try to update a different column it breaks. What is wrong (probably a lot!!) with this:
update TABLENAME
set Column2 = 'Hello'
where (select distinct Column1 from TABLENAME)
You can try to use ROW_NUMBER window function make row number then only update with row number is 1.
update t1
set Column2 = 'Hello'
FROM (
select *,ROW_NUMBER() OVER(PARTITION BY Column1 ORDER BY Column3) rn
from TABLENAME
) t1
where rn = 1
Results:
| Column1 | Column2 | Column3 |
|---------|---------|---------|
| a | Hello | 1 |
| a | (null) | 2 |
| b | Hello | 3 |
| b | (null) | 4 |
| c | Hello | 5 |
| c | (null) | 6 |
Assuming it is your unique index, use Column 3.
UPDATE tablename SET column2 = 'Hello' WHERE column3 IN
(SELECT column3 from TableName GROUP BY column1)
You could also update only the rows that have odd numbers in Column 3.
UPDATE tablename SET column2 = 'Hello' WHERE column3 % 2 != 0
You can also use CROSS APPLY and CTE (Common Table Expression) to achieve this:
;with CTE AS
(SELECT t.Column1, t.Column2
FROM (SELECT DISTINCT Column1
FROM TABLENAME) x
CROSS APPLY(SELECT TOP 1 *
FROM TABLENAME
WHERE column1 = x.column1) t)
UPDTATE CTE
SET Column2 = 'Hello'
SELECT * FROM TABLENAME
You could use a window function as
UPDATE TT
SET Col = B
FROM
(
SELECT Col, ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col) RN
FROM T
)TT INNER JOIN
(
VALUES (1, 'Hello'), (2, NULL)
) TVC (A, B)
ON TT.RN = TVC.A;
Results:
+-------+
| Col |
+-------+
| Hello |
| NULL |
| Hello |
| NULL |
| Hello |
| NULL |
+-------+
Or using a CASE expression as:
UPDATE TT
SET Col = CASE WHEN RN = 1 THEN 'Hello' END
FROM
(
SELECT Col, ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col) RN
FROM T
)TT;
Online Demo

SQL Server from X columns make 1 columns

I have example table like this:
Column1 Column2 Column3 Column4 Column5
---------------------------------------------------------------
Dog 456 Long Short Small
Car 454 Blue NULL NULL
Fruit 466 Apple Pear NULL
And I expect table like this when XY columns will be put into 1 column
Column1 Column2 Column3
------------------------------
Dog 456 Long
Dog 456 Short
Dog 456 Small
Car 454 Blue
Fruit 466 Apple
Fruit 466 Pear
Thank you for your opinions when tables have over thousand rows.
Even I can do this in excel and then back import data to SQL Server
You can use unpivot as below:
Select * from #unpivotdata
unpivot( cols for col in([column3],[column4],[column5])) u
Output as below:
+---------+---------+-------+
| Column1 | column2 | cols |
+---------+---------+-------+
| Dog | 456 | Long |
| Dog | 456 | Short |
| Dog | 456 | Small |
| Car | 454 | Blue |
| Fruit | 466 | Apple |
| Fruit | 466 | Pear |
+---------+---------+-------+
SELECT Column1, Column2, Column3 FROM table WHERE Column3 IS NOT NULL
UNION ALL
SELECT Column1, Column2, Column4 FROM table WHERE Column4 IS NOT NULL
UNION ALL
SELECT Column1, Column2, Column5 FROM table WHERE Column5 IS NOT NULL
Using UNION ALL:
SELECT col1, col2, col3
FROM tab
UNION ALL
SELECT col1, col2, col4
FROM tab
WHERE col4 IS NOT NULL
SELECT col1, col2, col5
FROM tab
WHERE col5 IS NOT NULL;
UnPivot would be more performant, but if the number of columns is unknowm.
You many notice that only the "Key" columns are identified, so the width is dynamic.
Example
Declare #YourTable Table ([Column1] varchar(50),[Column2] varchar(50),[Column3] varchar(50),[Column4] varchar(50),[Column5] varchar(50))
Insert Into #YourTable Values
('Dog',456,'Long','Short','Small')
,('Car',454,'Blue',NULL,NULL)
,('Fruit',466,'Apple','Pear',NULL)
Select A.[Column1]
,A.[Column2]
,[Column3] = C.Value
From #YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Field = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('Column1','Column2')
) C
Returns

SQL Query - Row to columns not really a pivot

I'm trying to move certain fields of an ID into columns, but it doesn't appear to match all the pivot examples I am finding. All the examples I can find use some form of a grouping on a field value. I want to use more of a placement regardless of the value in the field. I want to do this in a query without looping via code. Data source example (sorry couldn't figure out how to format a table on the post so I used a code snippet):
+----+--------+--------+
| ID | Field1 | Field2 |
+----+--------+--------+
| 1 | NULL | NULL |
| 2 | Jim | 321 |
| 2 | Jack | 54 |
| 2 | Sue | 985 |
| 2 | Gary | 654 |
| 3 | Herb | 332 |
| 3 | Chevy | 10 |
+----+--------+--------+
Result set I'm trying to generate:
+----+------+------+-------+------+------+------+
| ID | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
+----+------+------+-------+------+------+------+
| 1 | NULL | NULL | | | | |
| 2 | Jim | 321 | Jack | 54 | Sue | 985 |
| 3 | Herb | 332 | Chevy | 10 | | |
+----+------+------+-------+------+------+------+
SQL Fiddle: http://sqlfiddle.com/#!3/a225a/1
;with cte as (
select id
, field1
, field2
, ROW_NUMBER() over (partition by id order by field1, field2) r
from #t
)
select c1.id
, c1.field1 col1
, c1.field2 col2
, c2.field1 col3
, c2.field2 col4
, c3.field1 col5
, c3.field2 col6
from cte c1
left outer join cte c2 on c2.id = c1.id and c2.r = c1.r + 1
left outer join cte c3 on c3.id = c1.id and c3.r = c1.r + 2
where (c1.r % 3) = 1
Explanation
ROW_NUMBER() over (partition by id order by field1, field2) r. This line ensures that we have a column counting up from 1 for each id. This allows us to distinguish between the multiple rows.
The CTE is used to save typing the same statement for c1, c2 and c3.
The joins ensure that all items in a row have the same id, and that data for col1, col3 and col5 (likewise for col2, col4 and col6) is taken from consecutive rows. We're using left outer joins because there may not rows in the source table for these columns.
The where statement says to take the first row of each set of 3 for the data in c1 (with c2 and c3 thus being the second and third of each set, thanks to the earlier join).
Here's a solution using dynamic sql that works though I'm sure there's a better way to do it. Caution, it's a bit painful. First it builds the list of columns to pivot and select, builds the dynamic sql and runs it.
DECLARE #PivotColumns as varchar(max), #SelectColumns as varchar(max), #sql as varchar(max)
SELECT #PivotColumns = ISNULL(#PivotColumns + ',', '') + ColNum,
#SelectColumns = ISNULL(#SelectColumns + ',', '') + 'NULLIF(' + ColNum + ', ''NULL'') as ' + ColNum
from (select distinct 'Col' + cast(ROW_NUMBER() OVER (partition by id order by id) as varchar) as ColNum
from (select id,
isnull(field1,'NULL') as field1,
isnull(field2,'NULL') as field2
from weirdpivot) cols
unpivot
(
value
for col in (field1, field2)
) unpivoted) DistinctColumns
set #sql = '
select id, + ' + #SelectColumns + '
from (select
''Col'' + cast(ROW_NUMBER() OVER (partition by id order by id) as varchar) as colnum
,id
,value
from (select id,
isnull(field1,''NULL'') as field1,
isnull(field2,''NULL'') as field2
from weirdpivot) cols
unpivot
(
value
for col in (field1, field2)
) u) unpivoted
pivot
(
max(value)
for colnum in (' + #PivotColumns + ')
) p'
exec (#sql)

a sql query from oracle database

I have two columns(column1, column2) in my oracle database table named as demo. I want to select same column1 having different column2.
SQL> select * from demo;
column1 | column2
--------|--------
A | 10
A | 20
A | 30
A | 30
I want the following output with count(*):
column1 | column2
--------|--------
A | 10
A | 20
How can i do this? Any help is much appreciated
Try:
select column1, column2
from myTable
group by column1, column2
having count(*) = 1