I have a table similar to the following:
id key str_val date_val num_val
1 A a
1 B b
1 C 2012-01-01
1 D 1
2 A c
2 E d
2 C 2012-01-02
2 D 2
I need it to look like:
id A B C D E
1 a b 2012-01-01 1
2 c 2012-01-02 2 d
Basically, each key needs to become it's own column
I tried and failed to use the PIVOT command usefully, and I'm currently trying to accomplish this with a case statement. i.e.
select
id,
case key
when 'A'
then str_val
end as A,
case key
when 'C'
then date_val
end as C
--etc.
from test_table
However I can't figure out how to combine the table rows after this runs. I'm stuck with:
id A B C D E
1 a
1 b
1 2012-01-01
1 1
Any thoughts or input that could help me out? Thanks in advance.
You are 90% of the way there:
with cte as (
select
id,
case [key]
when 'A'
then str_val
end as A,
case [key]
when 'B'
then str_val
end as B,
case [key]
when 'C'
then date_val
end as C,
case [key]
when 'D'
then num_val
end as D,
case [key]
when 'E'
then str_val
end as E
from test_table
)
select id, max(A) as A, max(B) as B, max(C) as C, max(D) as D, max(E) as E
from cte
group by id
As long as id and key are a unique combination per table then you could write your query like:
SELECT ta.str_val as A,
tb.str_val as B,
tc.date_val as C,
td.num_val as D,
te.str_val as E
FROM (SELECT DISTINCT id FROM test_table) ids
LEFT JOIN test_table ta ON ids.id = ta.id AND ta.key = 'A'
LEFT JOIN test_table tb ON ids.id = tb.id AND tb.key = 'B'
LEFT JOIN test_table tc ON ids.id = tc.id AND tc.key = 'C'
LEFT JOIN test_table td ON ids.id = td.id AND td.key = 'D'
LEFT JOIN test_table tc ON ids.id = te.id AND te.key = 'E';
In this query you get all the IDs (if you can reply on column 'A' always being there you can start with that instead). Then you have to join on each key for the given id.
If you cannot rely on the data type of the key, i.e. A may be String or Date, then you have to use the following for each select:
COALESCE(ta.str_val,TO_CHAR(ta.date_val,'DD-MM-YYYY'),TO_CHAR(ta.num_val)) A,
COALESCE(tb.str_val,TO_CHAR(tb.date_val,'DD-MM-YYYY'),TO_CHAR(tb.num_val)) B,
...
etc.
Related
Question: I need to select only record with 'O' and also check the 'Actual Time' if record is 'D' then do not consider the record - 'O' output, see my expected output below (new to SQL)
SELECT DISTINCT Record, Actual Time
FROM app.abc
WHERE id = 100
Record Actual Time
-----------------------------------
D 2022-06-13 02:52:00.000
O 2022-06-13 02:52:00.000
O 2022-06-13 05:11:00.000
Expected output:
Record Actual Time
---------------------------------
O 2022-06-13 05:11:00.000
Okay,
;WITH [X] AS (
SELECT DISTINCT [Record], [Actual Time] FROM [app].[abc] WHERE [id]=100
)
SELECT
O.[Record],
O.[Actual Time]
FROM
[X] [O]
LEFT JOIN
(
SELECT
[Actual Time]
FROM
[X]
WHERE
[Record] = 'D'
) [D]
ON D.[Actual Time] != O.[Actual Time]
WHERE
O.[Record] = 'O';
You want to find O records without a matching D record. That is pretty much the definition of an anti-join. You can do:
select a.*
from abc a
left join abc b on b.actual_time = a.actual_time and b.record = 'D'
where a.record = 'O' and b.record is null
Result:
record actual_time
------ ---------------------------
O 2022-06-13 05:11:00.0000000
See running example at db<>fiddle.
Hello guys Im just starting with SQl server,
Sorce data
type num1 num2 date1
A 1 2 01/01/2019
b 1 01/02/2019
b 3 05/02/2017
a 1 1 03/02/2019
a 2 3 15/03/2018
b 2 20/12/2018
Expected result
type num1 num2 date1 'date2'
a 1 2 01/01/2019 20/12/2018
a 1 1 03/02/2019 01/02/2019
a 2 3 15/03/2018 05/02/2017
this is the best i made until now, and my date2 are all messed up im not getting the correct values , and i still have the problem with dates that the same number can exist in diferent years num1 1 type b exist in 2017 and 2019
select c1.type, c1.num1, c1.num2, c1.date, c2.date as 'date2'
from t1 c1
inner join t1 c2 on c2.num2=c1.num1
order by c1.type
Thanks for your help
I think this is as simple as:
SELECT
t1.[type],
t1.num1,
t1.num2,
t1.date1,
t2.date1 AS date2
FROM
t1
LEFT JOIN t2 ON t2.num1 = t1.num2 AND t2.num2 IS NULL
ORDER BY
t1.[type];
But note that I had to make the requirement up to some degree, as it wasn't entirely clear from your question.
What you need to do is join on yourself, then allow date
Declare #table1 table (type Char(1)
,num1 int
,num2 int
,date1 date)
insert into #table1 (type,num1,num2,date1)
values
('a',1,2,'2019-01-01')
,('b',1,null,'2019-01-02')
,('b',3,null,'2017-05-02')
,('a',1,1,'2019-03-02')
,('a',2,3,'2018-03-15')
,('b',2,null,'2018-12-20')
select t.*,t2.date1 'Date2'
from #table1 t
inner join #table1 t2
on t.num2 = t2.num1
and t.type = 'a'
and t2.type = 'b'
I have 3 tables in SQL Server:
map_table: (workflow map path)
stepId step_name
----------------
1 A
2 B
3 C
4 D
5 E
history_table:
stepId timestamp author
----------------------------
1 9:00am John
2 9:20am Mary
current_stageTable:
Id currentStageId waitingFor
------------------------------------
12345 3 Kat
I would like to write a query to show the map with the workflow status. Like this result here:
step name time author
----------------------------
1 A 9:00am John
2 B 9:20am Mary
3 C waiting Kat
4 D
5 E
I tried left join
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
I thought it will list all the records from the map table, since I am using left join, but somehow it only shows 3 records which is from history table..
So I changed to
select
m.stepId, m.step_name, h.timestamp, h.author
from
map_table m
left join
history_table h on m.stepId = h.stepId
union
select
m.stepId, m.step_name, '' as timestamp, '' as author
from
map_table m
where
m.stageId not in (select stageId from history_table)
order by
m.stepId
Then it list the result almost as I expected, but how do I add the 3rd table in to show the current active stage?
Thank you very much for all your help!! Much appreciated.
Looks like it's what you asked:
with map_table as (
select * from (values (1,'A')
,(2,'B')
,(3,'C')
,(4,'D')
,(5,'E')) t(stepId, step_name)
)
, history_table as (
select * from (values
(1,'9:00am','John')
,(2,'9:20am','Mary')) t(stepId, timestamp, author)
)
, current_stapeTable as (
select * from (values (2345, 3, 'Kat')) t(Id, currentStageId, waitingFor)
)
select
m.stepId, m.step_name
, time = coalesce(h.timestamp, case when c.waitingFor is not null then 'waiting' end)
, author = coalesce(h.author, c.waitingFor)
from
map_table m
left join history_table h on m.stepId = h.stepId
left join current_stapeTable c on m.stepId = c.currentStageId
I think a union fits well with the data and avoids the coalescing the values on multiple joins.
with timeline as (
select stepId, "timestamp" as ts, author from history_table
union all
select currentStageId, 'waiting', waitingFor from current_stageTable
)
select step_id, step_name, "timestamp", author
from
map_table as m left outer join timeline as t
on t.stepId = m.stepId
The following query which give 400 value of totalDeduction while the tblDeduction table have only 1 deduction with value 200
select
e.EmpID, concat(e.FName, ' ', e.LName) as Name,
o.HireDate, o.BasicPay, s.Scale,
SUM(a.amount) as totalAllowance,
SUM(d.Amount) as totalDeduction
from
tblEmployee e, tblEmpOfficialDetail o, tblScale s, tblAllowance a, tblDeduction d
where
e.EmpID = o.EmpID and
o.ScaleID = s.ID and
o.ScaleID = a.ScaleID and
o.ScaleID = d.ScaleID
group by
e.EmpID, e.FName, e.LName, o.HireDate, o.BasicPay, s.Scale
When I write a separate query using same the logic, it returns the correct ans.
select
sum(d.amount), o.scale
from
tblDeduction d, tblScale o
where
d.ScaleID = o.ID
group by
o.Scale
Please check it
Thanks
The two queries are not the same because the grouping condition is different.
It may be missing condition that result to inaccurate grouping.
I guessed from the first query that the relation between the tables is like:
tblEmployee e
|--> tblEmpOfficialDetail o
|-->tblDeduction d
|-->tblScale s
|-->tblAllowance a
Try the next query. It's the same but written as join condition to be sure from the relation between the joined tables.
select e.EmpID, concat(e.FName, ' ', e.LName) as Name ,
o.HireDate, o.BasicPay, s.Scale, SUM(a.amount) as totalAllowance,SUM(d.Amount) as totalDeduction
from tblEmployee e
join tblEmpOfficialDetail o on e.EmpID=o.EmpID
join tblDeduction d on o.ScaleID=d.ScaleID
join tblScale s on s.ID=o.ScaleID
join tblAllowance a on a.ScaleID = o.ScaleID
group by e.EmpID, e.FName, e.LName, o.HireDate, o.BasicPay, s.Scale
Is it possible to do a 1 on 1 element array concatenation if I have a query like this:
EDIT: Arrays not always have the same number of elements.
could be that array1 has sometimes 4 elements ans array2 8 elements.
drop table if exists a;
drop table if exists b;
create temporary table a as (select 1 as id,array['a','b','c'] as array1);
create temporary table b as (select 1 as id,array['x','y','z'] as array2);
select
a.id,
a.array1,
b.array2,
array_concat--This has to be a 1 to 1 ordered concatenation (see
--example below)
from a
left join b on b.id=a.id
What I would like to obtain here is a paired concatenation of both arrays 1 and 2, like this:
id array11 array2 array_concat
1 ['a','b','c'] ['d','e','f'] ['a-d','b-e','c-f']
2 ['x','y','z'] ['i','j','k'] ['x-i','y-j','z-k']
3 ...
I tried using unnest but i can't make it work:
select
a.id,
a.array1,
b.array2,
array_concat
from table a
left join b on b.id=a.id
left join (select a.array1,b.array2, array_agg(a1||b2)
FROM unnest(a.array1, b.array2)
ab (a1, b2)
) ag on ag.array1=a.array1 and ag.array2=b.array2
;
EDIT:
This works for only one table:
SELECT array_agg(el1||el2)
FROM unnest(ARRAY['a','b','c'], ARRAY['d','e','f']) el (el1, el2);
++Thanks to https://stackoverflow.com/users/1463595/%D0%9D%D0%9B%D0%9E
EDIT:
I came to a very close solution but it mixes up some of the intermediate values once the concatenation between arrays is done, never the less I still need a perfect solution...
The approach I am now using is:
1) Creating one table based on the 2 separate ones
2) aggregating using Lateral:
create temporary table new_table as
SELECT
id,
a.a,
b.b
FROM a a
LEFT JOIN b b on a.id=b.id;
SELECT id,
ab_unified
FROM pair_sources_mediums_campaigns,
LATERAL (SELECT ARRAY_AGG(a||'[-]'||b order by grp1) as ab_unified
FROM (SELECT DISTINCT case when a null
then 'not tracked'
else a
end as a
,case when b is null
then 'none'
else b
end as b
,rn - ROW_NUMBER() OVER(PARTITION BY a,b ORDER BY rn) AS grp1
FROM unnest(a,b) with ordinality as el (a,b,rn)
) AS sub
) AS lat1
order by 1;
Something like this.
with a_elements (id, element, idx) as (
select a.id,
u.element,
u.idx
from a
cross join lateral unnest(a.array1) with ordinality as u(element, idx)
), b_elements (id, element, idx) as (
select b.id,
u.element,
u.idx
from b
cross join lateral unnest(b.array2) with ordinality as u(element, idx)
)
select id,
array_agg(concat_ws('-', a.element, b.element) order by idx) as elements
from a_elements a
full outer join b_elements b using (id, idx)
group by coalesce(a.id, b.id);
The join operator using (..) will automatically take the non-null value from the joined tables. This removes the need to use e.g. coalesce(a.id, b.id).n
It's not pretty and definitely not efficient for large tables, but seems to do all you want.
For arrays that do not have the same amount of elements, the result will only have the element from one of the arrays.
For this dataset:
insert into a
(id, array1)
values
(1, array['a','b','c','d']),
(2, array['d','e','f']);
insert into b
(id, array2)
values
(1, array['x','y','z']),
(2, array['m','n','o','p']);
It returns this result:
id | elements
---+----------------
1 | {a-x,b-y,c-z,d}
2 | {d-m,e-n,f-o,p}
I think you were thinking too far, try this (SQLFiddle):
select
a.id,
a.array1,
b.array2,
array[a.array1[1] || '-' || b.array2[1],
a.array1[2] || '-' || b.array2[2],
a.array1[3] || '-' || b.array2[3]] array_concat
from
a inner join
b on b.id = a.id
;