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

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.

Related

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

Sequelize subquery in from clause

I am using POSTGRESQL with sequelize.js and would really like to execute a query which looks like this
SELECT "CT_Foo"."cola", "CT_Foo"."colb", "CT_Foo"."colc",
"CT_Foo"."cold", "CT_Foo"."cole", "CT_Foo"."colf",
COUNT(*)
FROM (
SELECT "CT_BAR"."cola", "CT_BAR"."colb", "CT_BAR"."colc",
"CT_BAR"."cold", "CT_BAR"."cole", "CT_BAR"."colf",
"CT_BAR"."colg"
FROM public."Customers" AS "CT_BAR"
WHERE ("CT_BAR"."colf" IN ('SOMEID') AND "CT_BAR"."colg" ?
'date')
) AS "CT_Foo"
WHERE ("CT_Foo"."colf" IN ('SOMEID'))
GROUP BY "CT_Foo"."cola", "CT_Foo"."colb", "CT_Foo"."colc",
"CT_Foo"."cold", "CT_Foo"."cole", "CT_Foo"."colf"
Columns A-F are text and G is JSONB
Basically the reason why I am doing this is because I need to group on columns A-F with a query on column F and if something exists in the JSONB in column G, I do not wish to include column G in the grouping, because it's JSON and I'm checking for the existence of a date... This seems a simple way to do this. I know postgresql has CTE's but sequelize does not support them. I believe another name for this is "Derived Tables"
I can form a query normally, but cannot get the subquery into the FROM clause.
Any idea on how todo this or get the same result?

Iterate through a list to get strings in SQL

I have a SQL table as shown below. I want to generate strings using the 2 fields in my table.
A B
M1 tiger
M1 cat
M1 dog
M3 lion
I want to read in this table, count the number of rows, and store it in string variables like String1 = M1_tiger, String2 = M1_cat, etc. What's the best way to do this?
You could do a concat type query.
SELECT (Table.A + '_' + Table.B) AS A_B, COUNT(*) AS RowsCount FROM Table
I'm asuming the your table name is "Table", the result where you will find the strings you want would be the column named A_B, each record will have two things in each record, one would be the string you asked for, the other column would always be the same thing, the total number of records on you table.
The count part is kinda easy but check this link so you can use the specific count you need: http://www.w3schools.com/sql/sql_func_count.asp
You can try this:
SELECT CONCAT(A, '_', B) FROM yourtable
When you say "read in this table", do you mean read it into a programming language like C#? Or do you want to dynamically create sql variables?
You may want to use a table variable to store your strings rather than individual variables. Regarding getting the row number, you could use something like:
WITH CTE AS
(
SELECT A, B,
ROW_NUMBER() OVER (order by OrderDate) AS 'RowNumber'
FROM MyTable
)
SELECT A,B,RowNumber FROM CTE
See this answer for more on how you may choose to use the table variable.
SQL: Dynamic Variable Names
If your are using Oracle, you can also do it like:
select A ||'_'||B
from yourTable
Solution for PostgreSQL
CREATE SEQUENCE one;
SELECT array_to_string(array_agg(concat('String',nextval('one'),' = ',A,'_',B)), ', ')
AS result
FROM test_table;
DROP SEQUENCE one;
Explanation:
Create a temporary sequence 'one' in order to use nextval function.
nextval('sequence') - advance sequence and return new value.
concat('input1', ...) - concatenate all arguments.
array_agg('input1', ...); - input values, including nulls,
concatenated into an array.
array_to_string('array', 'delimiter') - concatenates array elements
using supplied delimiter and optional null string.
Drop the sequence 'one'.
The output of the query (for two test rows in test_table):
result
-------------------------------------------
String1 = M1_tiger, String2 = M1_cat
(1 row)

SQL Server where column in where clause is null

Let's say that we have a table named Data with Id and Weather columns. Other columns in that table are not important to this problem. The Weather column can be null.
I want to display all rows where Weather fits a condition, but if there is a null value in weather then display null value.
My SQL so far:
SELECT *
FROM Data d
WHERE (d.Weather LIKE '%'+COALESCE(NULLIF('',''),'sunny')+'%' OR d.Weather IS NULL)
My results are wrong, because that statement also shows values where Weather is null if condition is not correct (let's say that users mistyped wrong).
I found similar topic, but there I do not find appropriate answer.
SQL WHERE clause not returning rows when field has NULL value
Please help me out.
Your query is correct for the general task of treating NULLs as a match. If you wish to suppress NULLs when there are no other results, you can add an AND EXISTS ... condition to your query, like this:
SELECT *
FROM Data d
WHERE d.Weather LIKE '%'+COALESCE(NULLIF('',''),'sunny')+'%'
OR (d.Weather IS NULL AND EXISTS (SELECT * FROM Data dd WHERE dd.Weather LIKE '%'+COALESCE(NULLIF('',''),'sunny')+'%'))
The additional condition ensures that NULLs are treated as matches only if other matching records exist.
You can also use a common table expression to avoid duplicating the query, like this:
WITH cte (id, weather) AS
(
SELECT *
FROM Data d
WHERE d.Weather LIKE '%'+COALESCE(NULLIF('',''),'sunny')+'%'
)
SELECT * FROM cte
UNION ALL
SELECT * FROM Data WHERE weather is NULL AND EXISTS (SELECT * FROM cte)
statement show also values where Wether is null if condition is not correct (let say that users typed wrong sunny).
This suggests that the constant 'sunny' is coming from end-user's input. If that is the case, you need to parameterize your query to avoid SQL injection attacks.

3 Table SQL Query, one line per object

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.