3 Table SQL Query, one line per object - sql

I have to query a database in which Objects are in one table, the Attributes of those objects in a 2nd table, and the values of those Attributes are in a 3rd table. Stupid, I know.
Table Info (the basics anyway)
OBJECT -- obj_id, name
ATTRIBUTE -- att_id, name
VALUE -- obj_id, att_id, value
I want a query in which the attributes of each object occur across the top of my results, and the values of those attributes are filled in for each object. At first I was thinking a simple 2 inner join query would work, but that will give me a result for each attribute of each object, whereas I want each object to it's own line.
My second thought is that I'm going to have to create a temporary table that basically combines the OBJECT and ATTRIBUTE table, then INSERT the info from the VALUE table, and then use the very simple query on my new temporary table.
Figured I'd post this on here first to get other thoughts and points of view.

You could potentially write a procedure that builds a dynamic SQL string by cursoring through the attribute table. That's about the only way that you could do this without hard-coding the attribute names.
If you don't mind hard-coding the attribute names there are two ways to go about it
Simple subqueries: (slightly easier to read and understand)
SELECT
o.name,
(SELECT value FROM Value v JOIN Attribute a ON v.att_id = a.att_id WHERE a.name = 'Attribute1' and v.obj_id = o.obj_id) [Attribute1],
(SELECT value FROM Value v JOIN Attribute a ON v.att_id = a.att_id WHERE a.name = 'Attribute2' and v.obj_id = o.obj_id) [Attribute2]
--etc...
FROM
[Object] o;
Or using PIVOT: (probably a bit faster)
WITH CTE AS (
SELECT
o.name ObjectName,
a.name AttributeName,
v.value AttributeValue
FROM
[Object] o
JOIN Value v ON v.obj_id = o.obj_id
JOIN Attribute a ON a.att_id = v.att_id
)
SELECT
ObjectName,
[Attribute1],
[Attribute2]
--etc
FROM
CTE
PIVOT
(
MAX(AttributeValue)
FOR AttributeName IN ([Attribute1],[Attribute2])
) as Results;
In these examples, 'Attribute1' and 'Attribute2' are the attribute names in the name column in the Attribute table.
If you did want to build a dynamic query to handle arbitrary attributes (if you can't hard-code) then you'd still be using one of these approaches as a template for that dynamic query. The PIVOT method would be much simpler to use in that case imo.

Related

How to select a column whose name is a value in another column in POSTGRESQL?

I know this isn't valid SQL, but I'd like to do something like:
SELECT items.{SELECT items.preferred_column}
To elaborate, to achieve what I'm trying to achieve, I could write a long case when statement:
SELECT
CASE WHEN items.preferred_column = "column_a" THEN items.column_a
CASE WHEN items.preferred_column = "column_b" THEN items.column_b
CASE WHEN items.preferred_column = "column_c" THEN items.column_c
... and so on...
But that seems wrong. I would prefer to write a query that looks at the value of items.preferred_column and loads that column.
Is this possible?
My use case involves an Active Record (the ORM for Rails) query, which limits me. I'm not able to use "INTO" for example.
Doing this without creating a SQL function would preferred, though if it's not possible without creating a SQL function that would be good to know.
Thanks in advance for lending your expertise!
You can try transforming the table rows with row_to_json() and then using json_each(), you can join the resultant "key" field on the preferred_column:
WITH CTE AS (
SELECT
row_to_json(Z.*)::jsonb as rcr,
row_number() over(partition by null order by <whatever comparator clause>) as rn,
Z.*
FROM items Z)
SELECT b.value, a.*
FROM CTE a, jsonb_each(rcr) b, CTE c
WHERE c.rn=a.rn AND b.key = ( c.preferred_column )
Note that this essentially operates as a quasi-pivot, so you'll need to maintain an index (the row_number invocation) to self-join the table when extracting the appropriate key-value pairs from jsonb_each's set-return. Casting to jsonb will be helpful in that the binary form will alphabetize the key-value pairs by key order within the object itself.
If you need to get the resultant value as a text string instead of a json primitive, you can do
b.value #>>'{}'
instead of using jsonb_each_text(), which will preserve any json columns.

How do I use SQL Server to select into another table?

I am consolidating a web service. I am replacing multiple calls to the service with one call that contains the data.
I have created a table:
CREATE TABLE InvResults
(
Invoices nvarchar(max),
InvoiceDetails nvarchar(max),
Products nvarchar(max)
);
I used (max) because I don't know how complex the json will get at this time.
I need to do some sort of selects like this (this is pseudocode, not actual SQL):
SELECT
(SELECT *
INTO InvResults for Column Invoices
FROM MyInvoiceTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoices')) AS invoices;
SELECT
(SELECT *
INTO InvResults for Column InvoiceDetails
FROM MyInvoiceDetailsTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoicedetails')) AS invoicedetails;
I don't know how to format this and my google skills are failing me at this point. I understand that I probably want to use an UPDATE statement, but I'm not sure how to do this in combination with the rest of my requirements. I'm exploring How do I UPDATE from a SELECT in SQL Server? but I am still at a halt.
The end result should be a table "InvResults" that has 3 columns containing one row with results from Select statements as JSON. The column names should be defined the same as the json root objects.
INSERT INTO InvResults(Invoices,InvoidesDetails)
SELECT
(SELECT *
INTO InvResults for Column Invoices
FROM MyInvoiceTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoices'))
,
(SELECT *
INTO InvResults for Column InvoiceDetails
FROM MyInvoiceDetailsTable
WHERE SomeColumns = 'someStuffvariable'
FOR JSON PATH, ROOT('invoicedetails'))
;
Because the SELECT.. FOR JSON is only returning 1 row above works.
The third field is easily to added, but left to do for yourself 😉

I need to create a VIEW from an existing TABLE and MAP an additional COLUMN to that VIEW

I am fairly new to SQL. What I am trying to do is create a view from an existing table. I also need to add a new column to the view which maps to the values of an existing column in the table.
So within the view, if the value in a field for Col_1 = A, then the value in the corresponding row for New_Col = C etc
Does this even make sense? Would I use the CASE clause? Is mapping in this way even possible?
Thanks
The best way to do this is to create a mapping or lookup table
For example consider the following LOOKUP table.
COL_A NEW_VALUE
---- -----
A C
B D
Then you can have a query like this:
SELECT A.*, LOOK.NEW_VALUE
FROM TABLEA AS A
JOIN LOOKUP AS LOOK ON A.COL_A = LOOK.COL_A
This is what DimaSUN is doing in his query too -- but in his case he is creating the table dynamically in the body of the query.
Also note, I'm using a JOIN (which is an inner join) so only results in the lookup table will be returned. This could filter the results. A LEFT JOIN there would return all data from A but some of the new columns might be null.
Generally, a view is an instance of a table/a replica provided that there is no alteration to the original table. So, as per your query you can manipulate the data and columns in a view by using case.
Create View viewname as
Select *,
case when column=a.value then 'C'
....
ELSE
END
FROM ( Select * from table) a
If You have restricted list of replaced values You may hardcode that list in query
select T.*,map.New_Col
from ExistingTable T
left join (
values
('A','C')
,('B','D')
) map (Col_1,New_Col) on map.Col_1 = T.Col_1
In this sample You hardcode 'A' -> 'C' and 'B' -> 'D'
In general case You better may to use additional table ( see Hogan answer )

I need to SELECT a group of columns not null

Basically, i have a table that have a series of columns named:
ATTRIBUTE10, ATTRIBUTE11, ATTRIBUTE12 ... ATTRIBUTE50
I want a query that gives me all the columns from ATTRIBUTE10 to ATTRIBUTE50 not null
As others have commented we aren't exactly sure of your requirements, but if you want a list the UNPIVOT can do that...
SELECT attribute , value
FROM
(SELECT * from YourFile) p
UNPIVOT
(value FOR attribute IN
(attribute1, attribute2, attribute3, etc.)
)AS unpvt
May be you can use where condition for all columns Or use between operator as below.
For All Columns
where ATTRIBUTE10 is not null and ATTRIBUTE11 is not null ...... and ATTRIBUTE50 is not null
By using between operator
where ATTRIBUTE10 between ATTRIBUTE11 and ATTRIBUTE50
One way to approach the problem is to unfold your table-with-a-zillion-like-named-attributes into one in which you've got one attribute per row, with appropriate foreign keys back to the original table. So something like:
CREATE TABLE ATTR_TABLE AS
SELECT ID_ATTR, ID_TABLE_WITH_ATTRS, ATTR
FROM (SELECT ((ID_TABLE_WITH_ATTRS-1)*100)+1 AS ID_ATTR, ID_TABLE_WITH_ATTRS, ATTRIBUTE10 AS ATTR FROM TABLE_WITH_ATTRS UNION ALL
SELECT ((ID_TABLE_WITH_ATTRS-1)*100)+2, ID_TABLE_WITH_ATTRS, ATTRIBUTE11 FROM TABLE_WITH_ATTRS UNION ALL
SELECT ((ID_TABLE_WITH_ATTRS-1)*100)+3, ID_TABLE_WITH_ATTRS, ATTRIBUTE12 FROM TABLE_WITH_ATTRS);
This only unfolds ATTRIBUTE10, ATTRIBUTE11, and ATTRIBUTE12, but you should be able to get the idea - the rest of the attributes just requires a little cut-n-paste on your part.
You can then query this table to find your non-NULL attributes as
SELECT *
FROM ATTR_TABLE
WHERE ATTR IS NOT NULL
ORDER BY ID_ATTR
Hopefully the difficulty you're encountering in dealing with this table-with-a-zillion-repeated-fields teaches you a hard lesson about exactly why tables with repeated fields or groups of fields are a Bad Idea.
dbfiddle here

SQL Select statement - base results upon the value of an alias within statement

I am trying to write a Select statement (comprised of around 20 different joined aliases) that will only return results if the value of one of the aliases created within the same statement equals a certain value.
I'm very green with SQL at this point and therefore don't really know how to phrase this dilemma properly to find the answer elsewhere.
Current code for element being assigned an alias of "cmp_freq":
ISNULL((SELECT GroupValue FROM ClientGroup WHERE ClientKey = c.ClientKey AND GroupCode = 'CMP-FREQ'),'PLEASE UPDATE FIELD') AS cmp_freq
Essentially, I only want results returned for the entire statement where the value of cmp_freq is "30". Is there any way to reference this alias in the where clause of the statement as a whole in order to accomplish this?
There are several ways to accomplish your goal. One way would be to wrap your query in a SELECT and use a WHERE clause, like so:
SELECT i.cmp_freq
FROM (
/* Your existing query */
SELECT
ISNULL((SELECT GroupValue FROM ClientGroup WHERE ClientKey = c.ClientKey AND GroupCode = 'CMP-FREQ'),'PLEASE UPDATE FIELD') AS cmp_freq
FROM MyTable c
) i
WHERE i.cmp_freq = 30
It's difficult to offer other options as there is not enough information in your question.