I have a table that looks like this:
Title01 Title02 Title03 Title04 Title05
Number Title Division Department IFC
And I am wanting to turn the columns into rows so it loos like this:
Field
Number
Title
Division
Department
IFC
Is it possible to do this using the PIVOT function in SQL?
I like to use CROSS APPLY for this:
select v.field
from t cross apply
(values (title01), (title02), (title03), (title04), (title05)
) v(field);
CROSS APPLY implements the lateral join. You can think of it as an extension of correlated subqueries -- but the subquery can return multiple columns and multiple rows. Unpivoting data happens to be a simple introduction to the concept.
Related
I would like to unpivot a SQL table around multiple columns.
I have tried a normal UNPIVOT statement but that only ppivots around one value.
See this link for example: https://codingsight.com/understanding-pivot-unpivot-and-reverse-pivot-statements.
I have tried to illustrate my data as well as my desired outcome in the picture below.
The top table is a sample of the data in the SQL table. I have used 3 materials but in reality there are 20.
The bottom table is my desired outcome.
The data is on a SQL 2008-r2 server.
Any pointers on how to go about this task?
Consider using cross apply, like so:
select t.date, t.product, x.*
from mytable t
cross apply (values
(container1material, container1amount),
(container2material, container2amount),
(container3material, container3amount)
) x(material, amount)
Use apply for unpivoting:
select t.date, t.product, v.*
from t cross apply
(values (container1amount, container1material),
(container2amount, container2material),
(container3amount, container3material)
) v(containeramount, containermaterial);
unpivot is bespoke syntax (not-standard) and it only does one thing. By contrast, lateral joins are very powerful and unpivoting is only one thing that you can do with them. Apply is worth learning about.
Most online documentation or tutorials discussing OUTER|CROSS APPLY describe something like:
SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … FROM …);
The subquery is normally a full SELECT … FROM … query.
I must have read somewhere that the subquery doesn’t need a FROM in which case the columns appear to come from the main query:
SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … );
because I have used it routinely as a method to pre-calculate columns.
The question is what is really happening if the FROM is omitted from the sub query? Is it short for something else? I found that it does not mean the same as from the main table.
I have a sample here: http://sqlfiddle.com/#!18/0188f7/4/1
First consider
SELECT o.name, o.type
FROM sys.objects o
Now consider
SELECT o.name, (SELECT o.type) AS type
FROM sys.objects o
A SELECT without a FROM is as though selecting from an imaginary single row table. The above doesn't change the results the scalar subquery just acts as a correlated sub query and uses the value from the outer query.
APPLY behaves in the same way. References to columns from the outer query are just passed in as correlated parameters. So this is the same as
SELECT o.name, ca.type
FROM sys.objects o
CROSS APPLY (SELECT o.type) AS ca
But APPLY in general is more capable than a scalar subquery in the SELECT (in that it can act to expand a row out or remove rows from the result)
What you have mentioned is not SUBQUERY. It is separate table expression. Whether you use FROM clause in the right expression or not problem.
If you use FROM clause in right table expression then you have got a source for the data in right table expression.
If you dont use FROM clause in the right expression, your source of data comes from left table expression.
First we will see what is APPLY operator. Reference BOL
Using APPLY
Both the left and right operands of the APPLY operator are table
expressions. The main difference between these operands is that the
right_table_source can use a table-valued function that takes a column
from the left_table_source as one of the arguments of the function.
The left_table_source can include table-valued functions, but it
cannot contain arguments that are columns from the right_table_source.
The APPLY operator works in the following way to produce the table
source for the FROM clause:
Evaluates right_table_source against each row of the left_table_source to produce rowsets.
The values in the right_table_source depend on left_table_source.
right_table_source can be represented approximately this way:
TVF(left_table_source.row), where TVF is a table-valued function.
Combines the result sets that are produced for each row in the evaluation of right_table_source with the left_table_source by
performing a UNION ALL operation.
The list of columns produced by the result of the APPLY operator is
the set of columns from the left_table_source that is combined with
the list of columns from the right_table_source.
Based on the way you are using APPLY operator, it will behave as correlated subquery or CROSS JOIN
Using values of the left table expression in right table expression
-- without FROM (similar to Correlated Subquery)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value) AS sq;
Not using values of left table expression in right table expression
-- FROM table (Similar to cross join)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value FROM test) AS sq;
Omitting the FROM statement is not specific to a CROSS/OUTER APPLY; any valid SQL select statement can omit it. By not using FROM you have no source for your data, so you can't specify columns within that source. Rather you can only select values that already exist; be that constants defined in the statement itself, or in some cases (e.g. subqueries) columns referenced from other parts of the query.
This is simpler to understand if you're familiar with Oracle's Dual table; a table with 1 row. In MS SQL that table would look like this:
-- Ref: https://blog.sqlauthority.com/2010/07/20/sql-server-select-from-dual-dual-equivalent/
CREATE TABLE DUAL
(
DUMMY VARCHAR(1) NOT NULL
, CONSTRAINT CHK_ColumnD_DocExc CHECK (DUMMY = 'X') -- ensure this column can only hold the value X
, CONSTRAINT PK_DUAL PRIMARY KEY (DUMMY) -- ensure we can only have unique values... combined with the above means we can only ever have 1 row
)
GO
INSERT INTO DUAL (DUMMY)
VALUES ('X')
GO
You can then do select 1 one, 'something else' two from dual. You're not really using dual; just ensuring that you have a table which will always return exactly 1 row.
Now in SQL anywhere you omit a FROM statement consider that statement as if it said FROM DUAL / it has the same meaning, only SQL allows this more shorthand approach.
Update
You mention in the comments that you don't see how you can reference columns from the original statement when in a subquery (e.g. of the kind you may see when using APPLY). The below code shows this without the APPLY scenario. Admittedly the demo code here's not somehting you'd ever use (since you could just to where Something like '%o%' on the original statement without needing the subquery/in statement), but for illustrative purposes it shows exactly the same sort of scenario as you've got with your APPLY scenario; i.e. the statement is just returning the value of SOMETHING for the current row.
declare #someTable table (
Id bigint not null identity(1,1)
, Something nvarchar(32) not null
)
insert #someTable (Something) values ('one'), ('two'), ('three')
select *
from #someTable x
where x.Something in
(
-- this subquery references the SOMETHING column from above, but doesn't have a FROM statement
-- note: there is only 1 value at a time for something here; not all 3 values at once; it's the same single value as Something as we have before the in keyword above
select Something
where Something like '%o%'
)
How could I query a JSON field to search in all fields of the JSON?
Table
id | JSON
--------------------------
1 | {"name":"toto","age":25,"surname":"toto2"}
I'd like to query on every fields of the JSON, because I'm programming a global search function, and i need for example to return every row containing "toto" in its JSON.
I managed to do it in SqlServer 2016 with OPEN_JSON function.
Here is my code
SELECT DISTINCT *
FROM ObjectsTable
CROSS APPLY OPENJSON([JSON]) as tbValues
WHERE tbValues.value like '%toto%'
I need to do the same thing in Postgres.
Essentially the same in Postgres, but cross apply is cross join lateral in the SQL standard and in Postgres
select o.id, t.*
from objectstable o
cross join lateral json_each_text(o.json) as t(k,val)
where t.val like '%toto%'
lets say I have a huge select on a certain table. One value for a column is calculated with complex logc and its called ColumnA. Now, for another column, I need the value from ColumnA and add some other static value to it.
Sample SQL:
select table.id, table.number, complex stuff [ColumnA], [ColumnA] + 10 .. from table ...
The [ColumnA] + 10 is what im looking for. The complex stuff is a huge case/when block.
Ideas?
If you want to reference a value that's computed in the SELECT clause, you need to move the existing query into a sub-SELECT:
SELECT
/* Other columns */,
ColumnA,
ColumnA + 10 as ColumnB
FROM
(select table.id, table.number, complex stuff [ColumnA].. from table ...
) t
You have to introduce an alias for this table (in the above, t, after the closing bracket) even if you're not going to use it.
(Equivalently - assuming you're using SQL Server 2005 or later - you can move your existing query into a CTE):
;WITH PartialResults as (
select table.id, table.number, complex stuff [ColumnA].. from table ...
)
SELECT /* other columns */, ColumnA, ColumnA+10 as ColumnB from PartialResults
CTEs tend to look cleaner if you've got multiple levels of partial computations being done, I.e. if you've now got a calculation that depends on ColumnB to include in your query.
Unfortunately, in SQL Server 2016:
SELECT 3 AS a, 6/a AS b;
Error: Invalid column name: 'a'.
You could solve this with a subquery and column aliases.
Here's an example:
SELECT MaxId + 10
FROM (SELECT Max(t.Id) As MaxId
FROM SomeTable t) As SomeTableMaxId
You could:
Do the + 10 in the client code
Write a scalar-valued function to encapsulate the logic for complex stuff. It will be optimized into a single call.
Copy complex stuff logic for the other column. It should get optimized out into 1 call.
Use a sub-select to apply the additional calculation
One convenient option to reuse scalar expressions in a query is to use APPLY (or LATERAL in standard SQL):
SELECT
table.id,
table.number,
[ColumnA],
[ColumnA] + 10
FROM
table
CROSS APPLY (SELECT complex stuff [ColumnA]) t
By "fixed vector" I mean a static list of values, like 1 through 24.
The current query looks like this (simplified)
SELECT Period, Profit FROM Projections
But the data is "sparse" — so there's not a row for every period.
What query will give me a row for peiods 1-24 every time, with zeros (or NULLs) where there's no data?
I would like to do this with just the query to avoid a mess of client code.
Thanks!
You could make a udf called udfRange(start int,count int) or something like that, and left-join to the output of the function.
Or for something really quick and dirty, you could join to a subselect that looked like
SELECT DATA.Period, P.Profit
FROM (
SELECT 1 AS Period
UNION SELECT 2
...
UNION SELECT 24) AS DATA
LEFT JOIN Projections P ON DATA.Period = P.Period
Why not create a 'Periods' lookup table, with values 1 - 24 (and any other columns that might be relevant, like a description of the period, or its name) then do a left outer join between the Periods lookup table and your projections table.