I'm attempting to transpose a column of text and values to row headers. I've researched the PIVOT and UNPIVOT function but this function relies on aggregation from what I've gathered. Below is what I'm interested in achieving.
Source Table Schema:
[ID] [Category] [TextName]
1 A u
1 B v
1 C w
2 A x
2 B y
2 C z
Resulting transpose:
[ID] [A] [B] [C]
1 u v w
2 x y z
Is this possible?
SELECT id,
MIN( CASE WHEN Category = 'A' THEN TextName END ) AS A,
MIN( CASE WHEN Category = 'B' THEN TextName END ) AS B,
MIN( CASE WHEN Category = 'C' THEN TextName END ) AS C
FROM Table
GROUP BY id;
This is still a kind of aggregation even that we have a single value per cell (row-column combination).
Min/Max will give you the desired values since any basic type including strings have definition of Min/Max.
select *
from t pivot (min([TextName]) for [Category] in (A,B,C)) p
+----+---+---+---+
| ID | A | B | C |
+----+---+---+---+
| 1 | u | v | w |
+----+---+---+---+
| 2 | x | y | z |
+----+---+---+---+
Related
The request below:
SELECT foos.id,bars.name
FROM foos
JOIN bar_foo ON (bar_foo.foo_id = id )
JOIN bars ON (bars.id = bar_foo.bar_id )
returns a list like this:
id | name
---+-----
1 | a
1 | b
2 | a
2 | y
2 | z
3 | a
3 | b
3 | c
3 | d
How to get the ids for which id must have at least a and b, and more generally the content of a given array ?
From the example above, I would get:
id | name
---+-----
1 | a
1 | b
3 | a
3 | b
3 | c
3 | d
For two values, you can use windowing boolean aggregation:
select *
from (
select f.id, b.name,
bool_or(b.name = 'a') over(partition by id) has_a,
bool_or(b.name = 'b') over(partition by id) has_b
from foos f
join bar_foo bf on bf.foo_id = f.id
join bars b on b.id = bf.bar_id
) t
where has_a and has_b
A more generic approach uses array aggregation:
select *
from (
select f.id, b.name,
array_agg(b.name) over(partition by id) arr_names
from foos f
join bar_foo bf on bf.foo_id = f.id
join bars b on b.id = bf.bar_id
) t
where arr_names #> array['a', 'b']
I have a database table with columns shaped as following:
| ID | name | A | B | C | D |
| 1 | foo | 1 | 0 | 0 | 1 |
| 2 | bar | 0 | 0 | 1 | 1 |
| 3 | foo | 1 | 1 | 0 | 0 |
| 4 | bar | 1 | 1 | 0 | 0 |
A, B, C and D are bit columns.
I need to get the name values of the rows of which there at least two and that both have at least one identical bit column set to true. the result set I want to get for the given example is as following:
| name |
| foo |
I can do the following:
SELECT l.name
FROM dummy l
INNER JOIN dummy r ON l.name = r.name
WHERE (l.A = 1 AND r.A = 1)
OR (l.B = 1 AND r.B = 1)
OR (l.C = 1 AND r.C = 1)
OR (l.D = 1 AND r.D = 1)
GROUP BY l.name
HAVING COUNT(*) > 1
But this gets unreadable soon since the table is massive. I was wondering if there was a bitwise solution to solve this
I suspect that your data model is wrong. It feels like A-D represent the same "type" of thing and so the data ought to be represented using a single column that contains the data values A-D and (if necessary) one column to store the 1 or 0, with separate rows for each A-D value. (But then, of course, we can use the presence of a row to indicate a 1 and the absence of the row to represent a 0).
We can use UNPIVOT to get this "better" structure for the data and then the query becomes trivial:
declare #t table (ID int not null, name char(3) not null, A bit not null, B bit not null,
C bit not null, D bit not null)
insert into #t(ID,name,A,B,C,D) values
(1,'foo',1,0,0,1),
(2,'bar',0,0,1,1),
(3,'foo',1,1,0,0),
(4,'bar',1,1,0,0)
;With ProperLayout as (
select ID,Name,Property,Value
from #t t
unpivot (Value for Property in (A,B,C,D)) u
where Value = 1
)
select name,Property
from ProperLayout
group by name,Property
having COUNT(*) > 1
Result:
name Property
---- ---------
foo A
(Note also that the top of my script is not much different in size to the sample data in your question but has the massive benefit that it's runnable)
In similar way you could also use Apply opertaor
SELECT a.name FROM table t
CROSS APPLY (
VALUES (name, 'A', A), (name, 'B', B), (name, 'C', C), (name, 'D', D)
)a(name , names , value)
WHERE a.value = 1
GROUP BY a.name, a.Names, a.value
HAVING COUNT(*) > 1
From your description, you seem to want:
SELECT l.name
FROM dummy l
GROUP BY l.name
HAVING SUM( CAST(A as int) ) >= 2 OR
SUM( CAST(B as int) ) >= 2 OR
SUM( CAST(C as int) ) >= 2 OR
SUM( CAST(D as int) ) >= 2 ;
This is based on the description. I don't know what the same result row has to do with the question.
It is not hard to read. It is just long.
This would be more efficient:
SELECT distinct l.name
FROM dummy l
INNER JOIN dummy r
ON l.name = r.name
and l.id < r.id
and ( (l.A = 1 AND r.A = 1)
OR (l.B = 1 AND r.B = 1)
OR (l.C = 1 AND r.C = 1)
OR (l.D = 1 AND r.D = 1)
)
order by l.name
You could build it up reading sys.columns
I don't think TSQL has any bitwise operators.
How can I unpivot in Postgresql without using UNION? I have more than 100 columns, and I am looking for a neat way to do it.
Given table:
id c1 c2 c3
1 X Y Z
2 A B C
3 Y C Z
Desired table:
id col
1 X
1 Y
1 Z
2 A
2 B
2 C
3 Y
3 C
3 Z
Use jsonb functions:
select id, value as col
from my_table
cross join jsonb_each_text(to_jsonb(my_table))
where key <> 'id';
id | value
----+-------
1 | X
1 | Y
1 | Z
2 | A
2 | B
2 | C
3 | Y
3 | C
3 | Z
(9 rows)
Db<>Fiddle.
In Postgres 9.3 or 9.4 use to_json() and json_each_text().
In versions 9.1 or 9.2 install hstore:
create extension if not exists hstore;
select id, value as col
from my_table
cross join each(hstore(my_table))
where key <> 'id';
Problem statement:
a table with N columns, K out of which are used in a criterion to determine pairs of rows
such a criterion involving the K columns can simply be if columns c_1, c2, .. c_k are equal for the two different rows part of a pair (the criterion itself is not relevant, only the fact that it must be used)
the requirement is to extract all potential pairs, but only once. This means that if for a row there are more than 2 potential other rows that can form a pair given the above criterion, then only one pair must be extracted
Simple example:
Input table:
A | B | C
x | y | z
w | y | z
u | y | z
u | v | z
v | v | z
Criterion: B and C columns must be the same for two rows to be part of a pair.
Output:
x | y | z
w | y | z
u | v | z
v | v | z
What hints do you have for solving the problem in pure SQL (or in the Oracle dialect, if specific features help)?
If you can use window analytic function:
CREATE TABLE TT1 (A VARCHAR(4), B VARCHAR(4), C VARCHAR(4))
INSERT INTO TT1 VALUES ('x','y','z')
INSERT INTO TT1 VALUES ('w','y','z')
INSERT INTO TT1 VALUES ('u','y','z')
INSERT INTO TT1 VALUES ('u','v','z')
INSERT INTO TT1 VALUES ('v','v','z')
INSERT INTO TT1 VALUES ('k','w','z')
SELECT A.A, A.B, A.C
FROM
(SELECT *, ROW_NUMBER() OVER (PARTITION BY B,C ORDER BY A DESC) RN, COUNT(*) OVER (PARTITION BY B,C ) RC
FROM TT1) A
WHERE A.RN <=2 AND RC>1
Output:
A B C
---- ---- ----
v v z
u v z
x y z
w y z
Use the COUNT() analytic function partitioning on those rows you want to match as pairs:
SELECT A, B, C
FROM (
SELECT t.*,
COUNT(*) OVER (
PARTITION BY B, C
ORDER BY A
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS current_rn,
COUNT(*) OVER (
PARTITION BY B, C
ORDER BY A
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING
) AS next_rn
FROM table_name t
)
WHERE MOD( current_rn, 2 ) = 0
OR MOD( next_rn, 2 ) = 0;
Output:
A B C
- - -
u y z
w y z
u v z
v v z
Hi i have a situation here with Oracle SQL to come out with the sql result as the following :-
Company No of Employees Group Derived Field
a 1 x
b 1 x
c 2 y
d 1 y
so based on the group if all the company has same no of employees then i want the derived field to be
true else false.
So for group x , if company a and b has the same no of employees then derived field for
a and b would be true. As for c and d because the no of employees is different so the derived field
should be false.
any help would be appreciated. thanks
You want to use an analytic function. I think this is what you want:
select t.*,
(case when min(NumEmployees) over (partition by grp) =
max(NumEmployees) over (partition by grp)
then 1
else 0
end) as DerivedField
from table t;
Note: I usually represent booleans as 0 and 1.
Here is a purely sql solution. Assume the column names are company,number,ggroup,test.
for convenience create a view of table t as
create view gnums as select count(distinct number)as gnum,count(number) as num, ggroup from t group by ggroup;
The this select
select a.*,b.gnum = 1 and b.num <> 1 as test from t a,gnums b where a.ggroup = b.ggroup;
yields
id | company | number | ggroup | test
----+---------+--------+--------+------
1 | a | 1 | x | t
2 | b | 1 | x | t
3 | c | 2 | y | f
4 | d | 1 | y | f
5 | e | 2 | z | f
I added row e to show that any group with only one company should yield false, assuming that's what you intended.