Here is my table in my Microsoft Access database:
Terr(Prim) Terr(Sec) Qty
---------- --------- -------
A A 0.5
A B 0.5
I want to change it to like this through SQL:
Type Terr Qty
----------- ---- ----
Original A 0.5
Original A 0.5
50-50 give A -0.5
50-50 take B 0.5
Rules:
Create two columns, [Type] and [Terr]
if [Terr(Prim)] = [Terr (Sec)], then keep the row and [Type] = "Original"
if [Terr (Prim)] <> [Terr (Sec)], then:
Row 1: [Type] = "Original", [Terr] = [Terr(Prim)], the other columns remain the same
Row 2: [Type] = "50-50 Give", [Terr] = [Terr (Prim)], [Qty] turns into negative
Row 3: [Type] = "50-50 take", [Terr] = [Terr (Sec)], the other columns remain the same
delete [Terr(Prim)] and [Terr (Sec)]
I think you can use a query like this:
SELECT "Original" As [Type], [TerrPrim] AS [Terr], Qty
FROM t
UNION ALL
SELECT "50-50 Give" As [Type], [TerrPrim] AS [Terr], -Qty
FROM t
WHERE [TerrPrim] <> [TerrSec]
UNION ALL
SELECT "50-50 take" As [Type], [TerrSec] AS Terr, Qty
FROM t
WHERE [TerrPrim] <> [TerrSec];
Related
I am running the below query and need to output the rows as columns. When I am adding the second MAX statement, I am getting an error ORA-00918 column ambiguously defined. Not sure what I am doing wrong. Any help will be appreciated.
SELECT * from (
SELECT a.REF_NUM as "Number", a.SUMMARY, a.DESC_SEARCH as "Description", a.Status, e.Label, e.Value
FROM ca a,
cr_prp e
WHERE
a.PERSID = e.OWNING_CR
AND a.CATEGORY = '16996807'
ORDER by a.REF_NUM DESC)t
PIVOT
(
MAX(CASE WHEN LABEL = 'Plan/UnPlanned' THEN Value END),
MAX(CASE WHEN LABEL = 'Reason for' THEN Value END),
MAX(CASE WHEN LABEL = 'Name & "ID"' THEN Value END)
FOR LABEL
IN ('Plan/UnPlanned',
'Reason for',
'Name & "ID"')
)
You probably need something like this
select * from tab
PIVOT
(
MAX(Value)
FOR LABEL
IN ('Plan/UnPlanned' as PU,
'Reason for' as R,
'Name & "ID"' as NI)
)
Number SUMMARY D STATUS PU R NI
---------- ---------- - ---------- ---------- ---------- ----------
2 2 z 2 1
1 1 x 1 2
2 2 y 2 1
I.e. 1) add alias to you pivot labels
do not multiplicate the MAX calculation, it it the responsibility of PIVOT to calculate max for each label.
Consider the following tables
ID_TYPE
ID
TYPE
1
3
2
3
3
1
4
2
5
2
ID_HISTORY
DEBIT_ID
DEBIT_LOCATION
AMOUNT
CREDIT_ID
CREDIT_LOCATION
MONTH
3
LOC1
100
1
LOC5
MAY
4
LOC2
200
3
LOC6
MAY
2
LOC3
300
5
LOC7
MAY
1
LOC4
400
3
LOC8
JUNE
3
LOC9
500
2
LOC10
JUNE
Now suppose I want to fetch all rows from ID_HISTORY in the MONTH of MAY, and result should contain only these columns:
Id, Location, Amount
Cases:
Result should contain rows only where either DEBIT_ID or CREDIT_ID is of TYPE=3 in ID_TYPE table
If the DEBIT_ID is of TYPE = 3 in the ID_TYPE table, then pick DEBIT_ID as "Id", else pick CREDIT_ID as "Id"
Similarly, If the DEBIT_ID is of TYPE 3 in the ID_TYPE table, then pick DEBIT_LOCATION as "Location", else pick CREDIT_LOCATION as "Location"
For example, above tables should result in the following:
Id
Location
Amount
1
LOC5
100
2
LOC3
300
I know that something like the following should work:
SELECT
(CASE
WHEN (Tab.DEBIT_ID IN (
SELECT ID
FROM ID_TYPE Typ
WHERE Typ.TYPE = 3)
) THEN Tab.DEBIT_ID
ELSE Tab.CREDIT_ID END
) "Id",
(CASE
WHEN (Tab.DEBIT_ID IN (
SELECT ID
FROM ID_TYPE Typ
WHERE Typ.TYPE = 3)
) THEN Tab.DEBIT_LOCATION
ELSE Tab.CREDIT_LOCATION END
) "Location",
Tab.AMOUNT "Amount"
FROM (
SELECT *
FROM ID_HISTORY Tab
WHERE Tab.MONTH = 'MAY'
--this block will be very complicated and contain complex multi-level queries to fetch data
)
But as you can see this will be inefficient as I have to basically duplicate the full case logic for each conditional columns. Also, this is no way "clean" in case there are a lot of similar columns.
Also, if the case logic is complex, it will be inefficient even further. It would be better if i could select multiple columns in THEN / ELSE cases. I tried doing that, but it just gives me "ORA-00913: too many values" error.
What would be the optimized version?
You could use a join to remove the sub-queries:
SELECT CASE
WHEN typ.id IS NOT NULL
THEN h.debit_id
ELSE h.credit_id
END AS id,
CASE
WHEN typ.id IS NOT NULL
THEN h.debit_location
ELSE h.credit_location
END AS location,
h.AMOUNT
FROM (
SELECT *
FROM ID_HISTORY
WHERE MONTH = 'MAY'
-- this block will be very complicated and contain complex multi-level queries to fetch data
) h
LEFT OUTER JOIN (
SELECT id
FROM id_type
WHERE type = 3
) typ
ON (h.debit_id = typ.id)
I'm stuck trying to transpose a set of rows into a table. In my stored procedure, I take a delimited string as input, and need to transpose it.
SELECT *
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|')
CROSS APPLY string_split(value,',')
From which I receive:
value value
123,4,1,0,0,5 123
123,4,1,0,0,5 4
123,4,1,0,0,5 1
123,4,1,0,0,5 0
123,4,1,0,0,5 0
123,4,1,0,0,5 5
324,2,0,0,0,4 324
324,2,0,0,0,4 2
324,2,0,0,0,4 0
324,2,0,0,0,4 0
324,2,0,0,0,4 0
324,2,0,0,0,4 4
The values delimited by | are client details. And within each client, there are six attributes, delimited by ,. I would like an output table of:
ClientId ClientTypeId AttrA AttrB AttrC AttrD
------------------------------------------------
123 4 0 0 0 5
324 2 0 0 0 4
What's the best way to go about this? I've been looking at PIVOT but can't make it work because it seems like I need row numbers, at least.
This answer assumes that row number function will "follow the order" of the string. If it does not you will need to write your own split that includes a row number in the resulting table. (This is asked on the official documentation page but there is no official answer given).
SELECT
MAX(CASE WHEN col = 1 THEN item ELSE null END) as ClientId,
MAX(CASE WHEN col = 2 THEN item ELSE null END) as ClientTypeId,
MAX(CASE WHEN col = 3 THEN item ELSE null END) as AttrA,
MAX(CASE WHEN col = 4 THEN item ELSE null END) as AttrB,
MAX(CASE WHEN col = 5 THEN item ELSE null END) as AttrC,
MAX(CASE WHEN col = 6 THEN item ELSE null END) as AttrD
FROM (
SELECT A.value as org, B.value as item,
ROW_NUMBER() OVER (partition by A.value) as col
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|') as A
CROSS APPLY string_split(A.value,',') as B
) X
GROUP BY org
You might get a message about nulls in aggregate function ignored. (I always forget which platforms care and which don't.) If you do you can replace the null with 0.
Note, this is not as fast and using a CTE to find the 5 commas in the string with CHARINDEX and then using SUBSTRING to extract the values. But I'm to lazy to write up that solution which I would need to test to get all the off by 1 issues right. Still, I suggest you do it that way if you have a big data set.
I know you already got it pretty much answered, but here you can find a PIVOT solution
select [ClientID],[ClientTypeId],[AttrA],[AttrB],[AttrC],[AttrD]
FROM
(
select case when ColumnRow = 1 then 'ClientID'
when ColumnRow = 2 then 'ClientTypeId'
when ColumnRow = 3 then 'AttrA'
when ColumnRow = 4 then 'AttrB'
when ColumnRow = 5 then 'AttrC'
when ColumnRow = 6 then 'AttrD' else null end as
ColumnRow,t.value,ColumnID from (
select ColumnID,z.value as stringsplit,b.value, cast(Row_number()
over(partition by z.value order by z.value) as
varchar(50)) as ColumnRow from (SELECT cast(Row_number() over(order by
a.value) as
varchar(50)) as ColumnID,
a.value
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|') a
)z
CROSS APPLY string_split(value,',') b
)t
) AS SOURCETABLE
PIVOT
(
MAX(value)
FOR ColumnRow IN ([ClientID],[ClientTypeId],[AttrA],[AttrB],[AttrC],
[AttrD])
)
AS
PivotTable
There is a table, MyTable(ID, Type, Date). Column 'Type' can have a value of 1 or 2.
Top 'x' rows, ordered by 'Date' and satisfying the following condition, have to be selected.('a' and 'b' are integer values)
The selected 'x' rows can contain only a maximum of 'a' Type 1 rows and 'b' Type 2 rows.(If a+b < x, then only a+b rows have to be selected.)
I might be completely wrong but I have an idea of doing this by having count() inside a WHERE clause. But I am not sure of how to do it.
How do I go about this problem?
UPDATE:
Example -
x = 5
Case 1:
a = 5, b = 5
Result: Rows 1,2,3,4,5
Case 2:
a = 4, b = 1
Result: Rows 1,2,4,6,8
Case 3:
a = 1, b = 5
Result: Rows 1,2,3,5,7
Case 4:
a = 2, b = 1
Result: Rows 1,2,4
You can do it by nesting a UNION inside another query:
select top #x *
from (
select top #a *
from table
where type = '1'
order by [date]
union
select top #b *
from table
where type = '2'
order by [date]
) t
order by [date]
I'm using Oracle 10g. I have a table with a number of fields of varying types. The fields contain observations that have been made by made about a particular thing on a particular date by a particular site.
So:
ItemID, Date, Observation1, Observation2, Observation3...
There are about 40 Observations in each record. The table structure cannot be changed at this point in time.
Unfortunately not all the Observations have been populated (either accidentally or because the site is incapable of making that recording). I need to combine all the records about a particular item into a single record in a query, making it as complete as possible.
A simple way to do this would be something like
SELECT
ItemID,
MAX(Date),
MAX(Observation1),
MAX(Observation2)
etc.
FROM
Table
GROUP BY
ItemID
But ideally I would like it to pick the most recent observation available, not the max/min value. I could do this by writing sub queries in the form
SELECT
ItemID,
ObservationX,
ROW_NUMBER() OVER (PARTITION BY ItemID ORDER BY Date DESC) ROWNUMBER
FROM
Table
WHERE
ObservationX IS NOT NULL
And joining all the ROWNUMBER 1s together for an ItemID but because of the number of fields this would require 40 subqueries.
My question is whether there's a more concise way of doing this that I'm missing.
Create the table and the sample date
SQL> create table observation(
2 item_id number,
3 dt date,
4 val1 number,
5 val2 number );
Table created.
SQL> insert into observation values( 1, date '2011-12-01', 1, null );
1 row created.
SQL> insert into observation values( 1, date '2011-12-02', null, 2 );
1 row created.
SQL> insert into observation values( 1, date '2011-12-03', 3, null );
1 row created.
SQL> insert into observation values( 2, date '2011-12-01', 4, null );
1 row created.
SQL> insert into observation values( 2, date '2011-12-02', 5, 6 );
1 row created.
And then use the KEEP clause on the MAX aggregate function with an ORDER BY that puts the rows with NULL observations at the end. whatever date you use in the ORDER BY needs to be earlier than the earliest real observation in the table.
SQL> ed
Wrote file afiedt.buf
1 select item_id,
2 max(val1) keep( dense_rank last
3 order by (case when val1 is not null
4 then dt
5 else date '1900-01-01'
6 end) ) val1,
7 max(val2) keep( dense_rank last
8 order by (case when val2 is not null
9 then dt
10 else date '1900-01-01'
11 end) ) val2
12 from observation
13* group by item_id
SQL> /
ITEM_ID VAL1 VAL2
---------- ---------- ----------
1 3 2
2 5 6
I suspect that there is a more elegant solution to ignore the NULL values than adding the CASE statement to the ORDER BY but the CASE gets the job done.
i dont know about commands in oracle but in sql you could use some how that
first use pivot table is contains consecutives numbers 0,1,2...
i'm not sure but in oracle the function "isnull" is "NVL"
select items.ItemId,
case p.i = 0 then observation1 else '' end as observation1,
case p.i = 0 then observation1 else '' end as observation2,
case p.i = 0 then observation1 else '' end as observation3,
...
case p.i = 39 then observation4 else '' as observation40
from (
select items.ItemId
from table as items
where items.item = _paramerter_for_retrive_only_one_item /* select one item o more item where you filter items here*/
group by items.ItemId) itemgroup
left join
(
select
items.ItemId,
p.i,
isnull( max ( case p.i = 0 then observation1 else '' end ), '' ) as observation1,
isnull( max ( case p.i = 1 then observation2 else '' end ), '' ) as observation2,
isnull( max ( case p.i = 2 then observation3 else '' end), '' ) as observation3,
...
isnull( max ( case p.i = 39 then observation4), '' ) as observation40,
from
(select i from pivot where id < 40 /*you number of columns of observations, that attach one index*/
)
as p
cross join table as items
lef join table as itemcombinations
on item.itemid = itemcombinations.itemid
where items.item = _paramerter_for_retrive_only_one_item /* select one item o more item where you filter items here*/
and (p.i = 0 and not itemcombinations.observation1 is null) /* column 1 */
and (p.i = 1 and not itemcombinations.observation2 is null) /* column 2 */
and (p.i = 2 and not itemcombinations.observation3 is null) /* column 3 */
....
and (p.i = 39 and not itemcombinations.observation3 is null) /* column 39 */
group by p.i, items.ItemId
) as itemsimplified
on itemsimplified.ItemId = itemgroup.itemId
group by itemgroup.itemId
About pivot table
create an pivot table, Take a look at that
pivot table schema
name: pivot columns: {i : datatype int}
How populate
create foo table
schema foo
name: foo column: value datatype varchar
insert into foo
values('0'),
values('1'),
values('2'),
values('3'),
values('4'),
values('5'),
values('6'),
values('7'),
values('8'),
values('9');
/* insert 100 values */
insert into pivot
select concat(a.value, a.value) /* mysql */
a.value + a.value /* sql server */
a.value | a.value /* Oracle im not sure about that sintax */
from foo a, foo b
/* insert 1000 values */
insert into pivot
select concat(a.value, b.value, c.value) /* mysql */
a.value + b.value + c.value /* sql server */
a.value | b.value | c.value /* Oracle im not sure about that sintax */
from foo a, foo b, foo c
the idea about pivot table can consult in "Transact-SQL Cookbook By Jonathan Gennick, Ales Spetic"
I have to admit that the above solution (by Justin Cave) is simpler and easier to understand but this is another good option
at the end like you said you solved