SELECT XMLSERIALIZE(
XMLELEMENT(
NAME "row",
XMLFOREST(
A.TITLE AS "title",
A.TAG as "tag" )))
FROM ARTICLES A;
Expected Result:
Instead of Mentioned Column name(TITLE ,TAG) ,shall we able to keep '*' (means - select * from Articles)
because my table contains 150 columns.
As #data_henrik said, your question it not clear ...
But If I got you right, you want to create a xml doc for all columns from your base table, without having to code all 150 columns in the query.
I don't know a direct way of doing it, but you can do it in a 2 step way.
1st step will be a SELECT statement, that will BUILD your final SELECT based on the columns from your base table..
Let's take DEPARTMENT table from sample db as an example:
db2 => select * from department
DEPTNO DEPTNAME MGRNO ADMRDEPT LOCATION
------ ------------------------------------ ------ -------- ----------------
A00 SPIFFY COMPUTER SERVICE DIV. 000010 A00 -
B01 PLANNING 000020 A00 -
C01 INFORMATION CENTER 000030 A00 -
...
it has 5 columns. The following select will build your final SELECT ...
select
'SELECT XMLROW( ' ||
listagg(rtrim(colname) || ' AS "' || lcase(rtrim(colname)) || '"' , ', ')
|| ' ) FROM DEPARTMENT'
from syscat.columns
where tabname = 'DEPARTMENT'
------------------------------------------------------------------------------------------------------------
SELECT XMLROW( ADMRDEPT AS "admrdept", DEPTNAME AS "deptname", DEPTNO AS "deptno", LOCATION AS "location", MGRNO AS "mgrno" ) FROM DEPARTMENT
If you execute the resulting SELECT. it will produce the xml, similar to what you want.
----------------------------------------------------------------------------------------------------------
<row><admrdept>A00</admrdept><deptname>SPIFFY COMPUTER SERVICE DIV.</deptname><deptno>A00</deptno><mgrno>000010</mgrno></row>
<row><admrdept>A00</admrdept><deptname>PLANNING</deptname><deptno>B01</deptno><mgrno>000020</mgrno></row>
<row><admrdept>A00</admrdept><deptname>INFORMATION CENTER</deptname><deptno>C01</deptno><mgrno>000030</mgrno></row>
Note:
I have used XMLROW, for simplicity.. instead of yours XMLSERIALIZE( XMLELEMEENT( XMLFOREST ... just as an example.. so you get the idea..
Related
I have one array with column values as
{james=UC/james,adam=C/james,chris=UC/james,john=U/james}
The above column values are not json. They are in string in the following form:
{ username=privilegestring/grantor }
How to convert above column into multiple rows
Edit #3:
Updated the query to specifically target pg_catalog.pg_namespace for acl permissions grants, via CTE pg_catalog. Currently this CTE is filtered in the where clause to select a single namespace name ('avengers'); if you want to select from multiple namespace names, you should be able to add them into the WHERE clause of this CTE directly, or in the case of wanting all namespace names, remove the clause altogether.
It's worth noting as well, that you will need to expand the case statements in access_privilege_types to handle all permissions cases: 'r', 'w', 'a', 'd', and 'x', for the operations: SELECT, UPDATE, INSERT, DELETE, REFERENCE, respectively.
Edit #2:
The final posted version of the query below should get you the data you want in the format that you want it in. I don't know how many possible values there are for the permissions types; if you have more than the two specified currently, you will need to expand the case statements in the CTE* access_privilege_types*. Obviously you'll also need to replace your table name within the query, etc.. Let me know if you run into any trouble and I'll help as necessary.
Edit #1:
Was able to validate that this query works in Redshift. Updated the query to break out separate rows by grantee and owner. The current version doesn't break out individual permissions by row yet -- Will take a look later tonight to see if I can get that working as well.
Original:
I don't have access to my Redshift cluster to test this at the moment, but I will when I get home. The general idea behind the following method, is to create a numbered index table to cross join against that will expand the data in the permissions field into a row-based representation.
I had inquired about the size limit, because this will currently only handle 10,000 possible delimited values, however you can adjust the CTEs to scale up to larger amounts if needed for your specific application:
Revision 3 Query:
WITH
pg_namespace AS (
SELECT
nspname
, nspowner
, rtrim(ltrim(array_to_string(nspacl, ','), '{'), '}') as nspacl
FROM pg_catalog.pg_namespace
WHERE nspname = 'public'
),
-- Generating a table with the numbers 1 - 10 in a single column.
ten_numbers AS (
SELECT
1 AS num
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 0
),
-- Expands the values in ten_numbers to create a single column with the values 1 - 10,000.
depivot_index AS (
SELECT
(1000 * t1.num) + (100 * t2.num) + (10 * t3.num) + t4.num AS gen_num
FROM ten_numbers AS t1
JOIN ten_numbers AS t2 ON 1 = 1
JOIN ten_numbers AS t3 ON 1 = 1
JOIN ten_numbers AS t4 ON 1 = 1
),
-- Filters down generated_numbers to house only the numbers up to the maximum times that the delimiter appears.
splitter AS (
SELECT
*
FROM depivot_index
WHERE gen_num BETWEEN 1 AND (
SELECT max(REGEXP_COUNT(nspacl, '\\,') + 1)
FROM pg_namespace
)
),
-- Cross joins permissions_groups and splitter to populate all requests, delimited on ','.
expanded_input AS (
SELECT
pg.nspname
, pg.nspacl
, trim(split_part(pg.nspacl, ',', s.gen_num)) AS raw_permissions_string
FROM pg_namespace AS pg
JOIN splitter AS s ON 1 = 1
WHERE split_part(nspacl, ',', s.gen_num) <> ''
),
-- Breaks out the owner and grantee fields into their own columns respectively.
users_with_raw_permissions_data AS (
SELECT
e.raw_permissions_string
, e.nspname
, trim(split_part(e.raw_permissions_string, '=', 1)) AS grantee
, trim(split_part(trim(split_part(e.raw_permissions_string, '=', 2)), '/', 2)) AS owner
, trim(split_part(trim(split_part(e.raw_permissions_string, '=', 2)), '/', 1)) AS raw_permissions_data
FROM
expanded_input e
),
-- Mines privilege types from raw string data.
access_privilege_types AS (
SELECT
u.nspname
, u.owner
, u.grantee
,CASE
WHEN position('C*' IN u.raw_permissions_data) > 0 THEN 'C*'
WHEN position('U*' IN u.raw_permissions_data) > 0 THEN 'U*'
WHEN position('C' IN u.raw_permissions_data) > 0 THEN 'C'
WHEN position('U' IN u.raw_permissions_data) > 0 THEN 'U'
ELSE u.raw_permissions_data
END AS first_access_privilege
, CASE
WHEN position('U*' IN u.raw_permissions_data) > 0 THEN 'U*'
WHEN position('C*' IN u.raw_permissions_data) > 0 THEN 'C*'
WHEN position('U' IN u.raw_permissions_data) > 0 THEN 'U'
WHEN position('C' IN u.raw_permissions_data) > 0 THEN 'C'
ELSE u.raw_permissions_data
END AS second_access_privilege
, first_access_privilege || ',' || second_access_privilege AS merged_access_privileges
FROM users_with_raw_permissions_data u
),
-- Cross joins access_privilge_types and splitter to populate all privilege_types, delimited on ','.
expanded_access_privilege_types AS (
SELECT
a.nspname
, a.owner
, a.grantee
, trim(split_part(a.merged_access_privileges, ',', s.gen_num)) AS access_privileges
FROM access_privilege_types AS a
JOIN splitter AS s ON 1 = 1
WHERE split_part(a.merged_access_privileges, ',', s.gen_num) <> ''
GROUP BY 1, 2, 3, 4
)
SELECT
ea.nspname
, ea.owner
, ea.grantee
, LEFT(ea.access_privileges, 1) AS access_privilege
, CASE
WHEN POSITION('*' IN ea.access_privileges) > 0 THEN 'YES'
ELSE 'NO'
END AS is_grantable
FROM expanded_access_privilege_types ea
ORDER BY 1, 2, 3, 4, 5
Edit #4:
Adding some clarification on how the ten_numbers, depivot_index, and splitter tables work to break apart the pg_catalog.pg_namespace.nspacl field. The general overview, is that ten_numbers and depivot_index are created purely to return tables with numbered rows to use as an index when joining in thesplit_partvalues ofnspacl`.
ten_numbers generates a table with a single column, containing the numbers 0-9:
-------
| num |
-------
| 0 |
-------
| 1 |
-------
| etc |
-------
| 9 |
-------
This table is then expanded to house the range 0-9999 during the CTE depivot_index:
-----------
| gen_num |
-----------
| 0 |
-----------
| 1 |
-----------
| 2 |
-----------
| etc |
-----------
| 9998 |
-----------
| 9999 |
-----------
splitter then narrows down the table to house only the numbers up to the maximum count of the specified delimiter within the nspacl field:
-------
| num |
-------
| 0 |
-------
| 1 |
-------
| etc |
-------
| 6 |
-------
The table returned by splitter is then used as the target of a CROSS JOIN via the join on 1 = 1 in CTE expanded_input. This ensures that each member returned by split_part will have its own row:
---------------------------------------------------------------------------
| nspname | nspacl | raw_permissions_string |
---------------------------------------------------------------------------
| avengers | "{james=UC/james,adam=C/james}" | "james=UC/james" |
---------------------------------------------------------------------------
| avengers | "{james=UC/james,adam=C/james}" | "adam=C/james" |
---------------------------------------------------------------------------
| avengers | etc. | etc. |
---------------------------------------------------------------------------
I have a UDTF that will always return 1 row of 6 columns
The UDTF has one parameter
I want the 5 columns included in the result set of a query. The table I'm querying has a column that I want to use as the parameter for each row
I have not been able to figure out the correct syntax.
Any suggestions?
The UDTF
create function xxxx.UF_yyyyyy(USERID CHAR(10))
returns table (
p2User char(10),
STATUS CHAR(3),
USED DEC(7, 0),
CREATED DEC(7, 0),
SIGNON DEC(7, 0),
EXCLUDE DEC(7, 0))
language RPGLE
NOT DETERMINISTIC
NO SQL
DISALLOW PARALLEL
NOT FENCED
EXTERNAL NAME 'xxxx/UF_yyyyyy'
PARAMETER STYLE DB2SQL
example
select * from table(xxxx/UF_yyyyyy(CHAR('CMFIRST '))) a
result
P2USER STATUS USED CREATED SIGNON EXCLUDE
---------- ------ ------- ------- ------- -------
CMFIRST ACT 1170926 1150826 1170926 0
Here is an example of a select I tried
SELECT T1.AQABVN, T1.AQA8TX,
(SELECT COUNT(*) FROM fffff T4 WHERE T4.BDABVN = T1.AQABVN) AS SACCMS,
t2.p2User, t2.used
FROM
zzzzz T1
full join table(xxxx.UF_yyyyyy(T1.AQABVN)) t2 on T1.AQABVN = t2.p2User
Result
[SQL0205] Column P2USER not in table T2 in *N.
Got it working
SELECT T1.AQABVN, T1.AQA8TX,
(SELECT COUNT(*) FROM fffff T4 WHERE T4.BDABVN = T1.AQABVN) AS SACCMS,
t2.status, t2.used, t2.created, t2.signon, t2.exclude
FROM
zzzzz T1
join
table(SMLFQA.UF_XAJKUPR(T1.AQABVN)) t2 on T1.AQABVN = t2.p2User
I'm returning a unique list of id's from the users table, where specific columns in a related table (positions) contain a matching string.
The related table may have multiple records for each user record.
The query is taking a really really long time (its not scaleable), so I'm wondering if I'm structuring the query wrong in some fundamental way?
Users Table:
id | name
-----------
1 | frank
2 | kim
3 | jane
Positions Table:
id | user_id | title | company | description
--------------------------------------------------
1 | 1 | manager | apple | 'Managed a team of...'
2 | 1 | assistant | apple | 'Assisted the...'
3 | 2 | developer | huawei | 'Build a feature that...'
For example: I want to return the user's id if a related positions record contains "apple" in either the title, company or description columns.
Query:
select
distinct on (users.id) users.id,
users.name,
...
from users
where (
select
string_agg(distinct users.description, ', ') ||
string_agg(distinct users.title, ', ') ||
string_agg(distinct users.company, ', ')
from positions
where positions.users_id::int = users.id
group by positions.users_id::int) like '%apple%'
UPDATE
I like the idea of moving this into a join clause. But what I'm looking to do is filter users conditional on below. And I'm not sure how to do both in a join.
1) finding the keyword in title, company, description
or
2) finding the keyword with full-text search in an associated string version of a document in another table.
select
to_tsvector(string_agg(distinct documents.content, ', '))
from documents
where users.id = documents.user_id
group by documents.user_id) ## to_tsquery('apple')
So I was originally thinking it might look like,
select
distinct on (users.id) users.id,
users.name,
...
from users
where (
(select
string_agg(distinct users.description, ', ') ||
string_agg(distinct users.title, ', ') ||
string_agg(distinct users.company, ', ')
from positions
where positions.users_id::int = users.id
group by positions.users_id::int) like '%apple%')
or
(select
to_tsvector(string_agg(distinct documents.content, ', '))
from documents
where users.id = documents.user_id
group by documents.user_id) ## to_tsquery('apple'))
But then it was really slow - I can confirm the slowness is from the first condition, not the full-text search.
Might not be the best solution, but a quick option is:
SELECT DISTINCT ON ( u.id ) u.id,
u.name
FROM users u
JOIN positions p ON (
p.user_id = u.id
AND ( description || title || company )
LIKE '%apple%'
);
Basically got rid of the subquery, unnecessary string_agg usage, grouping on position table etc.
What it does is doing conditional join and removing duplicate is covered by distinct on.
PS! I used table aliases u and p to shorten the example
EDIT: adding also WHERE example as requested
SELECT DISTINCT ON ( u.id ) u.id,
u.name
FROM users u
JOIN positions p ON ( p.user_id = u.id )
WHERE ( p.description || p.title || p.company ) LIKE '%apple%'
OR ...your other conditions...;
EDIT2: new details revealed setting new requirements of the original question. So adding new example for updated ask:
Since you doing lookups to 2 different tables (positions and uploads) with OR condition then simple JOIN wouldn't work.
But both lookups are verification type lookups - only looking does %apple% exists, then you do not need to aggregate and group by and convert the data.
Using EXISTS that returns TRUE for first match found is what you seem to need anyway. So removing all unnecessary part and using with LIMIT 1 to return positive value if first match found and NULL if not (latter will make EXISTS to become FALSE) will give you same result.
So here is how you could solve it:
SELECT DISTINCT ON ( u.id ) u.id,
u.name
FROM users u
WHERE EXISTS (
SELECT 1
FROM positions p
WHERE p.users_id = u.id::int
AND ( description || title || company ) LIKE '%apple%'
LIMIT 1
)
OR EXISTS (
SELECT 1
FROM uploads up
WHERE up.user_id = u.id::int -- you had here reference to table 'document', but it doesn't exists in your example query, so I just added relation to 'upoads' table as you have in FROM, assuming 'content' column exists there
AND up.content LIKE '%apple%'
LIMIT 1
);
NB! in your example queries have references to tables/aliases like documents which doesn't reflect anywhere in the FROM part. So either you have cut in your example real query with wrong naming or you have made other way typo is something you need to verify and adjust my example query accordingly.
I have the following crosstab query in Access:
Transform Count(1) as Count
Select Cust,[Cust#],EntryDate,CloseDate
from Tbl1,Dates
where EntryDate>=[start date]
Group by Cust,[Cust#],EntryDate,CloseDate
Order by EntryDate
Pivot Quote;
I am having difficulty converting this to T-SQL.
Should I be using SSIS for Pivot transformation in order to solve this,
or do we have an equivalent SQL Server query for this?
We don't really have enough information to convert that specific crosstab query, so here is a simple example that may help you achieve your goal:
For a table named [Vehicles] containing...
VehicleID VehicleMake VehicleModel VehicleType
--------- ----------- ------------ ------------
1 Ford Focus Compact car
2 Ford F-150 Pickup truck
3 Dodge RAM 1500 Pickup truck
4 Toyota Tundra Pickup truck
5 Toyota Prius Hybrid car
6 Toyota Tacoma Pickup truck
...the Access crosstab query...
TRANSFORM Count(Vehicles.VehicleID) AS CountOfVehicleID
SELECT Vehicles.VehicleType
FROM Vehicles
GROUP BY Vehicles.VehicleType
PIVOT Vehicles.VehicleMake;
...returns:
VehicleType Dodge Ford Toyota
------------ ----- ---- ------
Compact car 1
Hybrid car 1
Pickup truck 1 1 2
The following T-SQL script accomplishes the same thing
DECLARE
#ColumnList AS NVARCHAR(MAX),
#SQL AS NVARCHAR(MAX)
-- build the list of column names based on the current contents of the table
-- e.g., '[Dodge],[Ford],[Toyota]'
-- required by PIVOT ... IN below
-- ref: http://stackoverflow.com/a/14797796/2144390
SET #ColumnList =
STUFF(
(
SELECT DISTINCT ',' + QUOTENAME([VehicleMake])
FROM [Vehicles]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),
1,
1,
'')
SET #SQL = '
WITH rollup
AS
(
SELECT VehicleMake, VehicleType, COUNT(VehicleID) AS n FROM [Vehicles]
GROUP BY VehicleMake, VehicleType
)
SELECT * FROM rollup
PIVOT (SUM([n]) FOR [VehicleMake] IN (' + #ColumnList + ')) AS Results'
EXECUTE(#SQL)
It returns:
VehicleType Dodge Ford Toyota
------------ ----- ---- ------
Compact car NULL 1 NULL
Hybrid car NULL NULL 1
Pickup truck 1 1 2
drop table #tmpT1
select distinct UserField2 into #tmpT1 from CreateExcelForm
--select * from #tmpT1
DECLARE #PivotColumnHeaders VARCHAR(MAX)
SELECT #PivotColumnHeaders =
COALESCE(
#PivotColumnHeaders + ',[' + cast(UserField2 as varchar) + ']',
'[' + cast(UserField2 as varchar)+ ']'
)
FROM #tmpT1
print(#PivotColumnHeaders)
DECLARE #PivotTableSQL NVARCHAR(MAX)
SET #PivotTableSQL = N'
SELECT *
FROM (
SELECT
* from CreateExcelForm
) AS PivotData
PIVOT (
max(StockCode)
FOR UserField2 IN (
' + #PivotColumnHeaders + '
)
) AS PivotTable
'
EXECUTE(#PivotTableSQL)
I'm having trouble converting a MS Access pivot table over to SQL Server. Was hoping someone might help..
TRANSFORM First(contacts.value) AS FirstOfvalue
SELECT contacts.contactid
FROM contacts RIGHT JOIN contactrecord ON contacts.[detailid] = contactrecord.[detailid]
GROUP BY contacts.contactid
PIVOT contactrecord.wellknownname
;
Edit: Responding to some of the comments
Contacts table has three fields
contactid | detailid | value |
1 1 Scott
contactrecord has something like
detailid | wellknownname
1 | FirstName
2 | Address1
3 | foobar
contractrecord is dyanamic in that the user at anytime can create a field to be added to contacts
the access query pulls out
contactid | FirstName | Address1 | foobar
1 | Scott | null | null
which is the pivot on the wellknownname. The key here is that the number of columns is dynamic since the user can, at anytime, create another field for the contact. Being new to pivot tables altogether, I'm wondering how I can recreate this access query in sql server.
As for transform... that's a built in access function. More information is found about it here. First() will just take the first result on that matching row.
I hope this helps and appreciate all the help.
I quick search for dynamic pivot tables comes up with this article.
After renaming things in his last query on the page I came up with this:
DECLARE #PivotColumnHeaders VARCHAR(max);
SELECT #PivotColumnHeaders = COALESCE(#PivotColumnHeaders + ',['+ CAST(wellknownname as varchar) + ']','['+ CAST(wellknownname as varchar) + ']')
FROM contactrecord;
DECLARE #PivotTableSQL NVARCHAR(max);
SET #PivotTableSQL = N'
SELECT *
FROM (
SELECT
c.contactid,
cr.wellknownname,
c.value
FROM contacts c
RIGHT JOIN contactrecord cr
on c.detailid = cr.detailid
) as pivotData
pivot(
min(value)
for wellknownname in (' + #PivotColumnHeaders +')
) as pivotTable
'
;
execute(#PivotTableSQL);
which despite its ugliness, it does the job