I have a table with the name of other tables in one column.
So now I need check if these tables have certain value in the first row.
After that if the table has that value I can show that table as a row in the first table; for example on MAIN TABLE, I put all the names of the other tables.
I expect if the tables has the value 1 in the first row it will be returned.
The number of tables change always, and the first value in the other tables change as well
The example is something like this
MAIN TABLE
------------
| table_name|
| a |
| b |
| c |
| d |
a
--------
| value|
--------
| 0 |
b
--------
| value|
--------
| 1 |
c
--------
| value|
--------
| 1 |
d
--------
| value|
--------
| 0 |
RESULT EXPECTED
------------
| table_name|
| b |
| c |
Assuming the main table is named "names", other tables have names 'a', 'b', ... (as in your question), and value columns in a, b, c, ... are named 'myvalue', you will get the result with this function:
create or replace function have_ones(tcTableName text)
returns boolean as
$body$
declare
lnResult int;
begin
lnResult := 0;
execute ('select count(*) from ' || tcTableName || ' where myvalue = 1;') into lnResult;
return ( lnResult > 0 );
end;
$body$
language plpgsql volatile;
Function uses dynamically created query, that depends on table name.
With this function defined, you will get result in following query:
select table_name from names where have_ones(names.table_name);
Related
I'm struggling to find a value that might be in different tables but using UNION is a pain as there are a lot of tables.
[Different table that contains the suffixes from the TestTable_]
| ID | Name|
| -------- | -----------|
| 1 | TestTable1 |
| 2 | TestTable2 |
| 3 | TestTable3 |
| 4 | TestTable4 |
TestTable1 content:
| id | Name | q1 | a1 |
| -------- | ---------------------------------------- |
| 1 | goose | withFeather? |featherID |
| 2 | rooster| withoutFeather?|shinyfeatherID |
| 3 | rooster| age | 20 |
TestTable2 content:
| id | Name | q1 | a1 |
| -------- | ---------------------------------------------------|
| 1 | brazilian_goose | withFeather? |featherID |
| 2 | annoying_rooster | withoutFeather?|shinyfeatherID |
| 3 | annoying_rooster | no_legs? |dead |
TestTable3 content:
| id | Name | q1 | a1 |
| -------- | ---------------------------------------- |
| 1 | goose | withFeather? |featherID |
| 2 | rooster| withoutFeather?|shinyfeatherID |
| 3 | rooster| age | 15 |
Common columns: q1 and a1
Is there a way to parse through all of them to lookup for a specific value without using UNION because some of them might have different columns?
Something like: check if "q1='age'" exists in all those tables (from 1 to 50)
Select q1,*
from (something)
where q1 exists in (TestTable_*)... or something like that.
If not possible, not a problem.
You could use dynamic SQL but something I do in situations like this where I have a list of tables that I want to quickly perform the same actions on is to either use a spreadsheet to paste the list of tables into and type a query into the cell with something like #table then use the substitute function to replace it.
Alternative I just paste the list into SSMS and use SHIFT+ALT+ArrowKey to select the column and start typing stuff out.
So here is my list of tables
Then I use that key combo. As you can see my cursor has now selected all those rows.
Now I can start typing and all rows selected will get the input.
Then I just go to the other side of the table names and repeat the action
It's not a perfect solution but it's quick a quick and dirty way of doing something repetitive quickly.
If you want to find all the tables with that column name you can use information schema.
Select table_name from INFORMATION_SCHEMA.COLUMNS where COLUMN_NAME = 'q1'
Given the type of solution you are after I can offer a method that I've had to use on legacy systems.
You can query sys.columns for the name of the column(s) you need to find in N tables and join using object_id to sys.tables where type='U'. This will give you a list of table names.
From this list you can then build a working query for each table, and depending on your requirements (is this ad-hoc?) either just manually execute it yourself of build a procedure that will do it for you using sp_executesql
Eg
select t.name, c.name
into #workingtable
from sys.columns c
join sys.tables t on t.object_id=c.object_id
where c.name in .....
psudocode:
begin loop while rows exist in #working table
select top 1 row from #workingtable
set #sql=your query specific to that table and column(s)
exec(#sql) / sp_executesql / try/catch as necessary
delete row from working table
end loop
Hopefully that give ideas at least for how you might implement your requirements.
I have one table with two columns id and data. The values in the table are as follows:
id|data|
1 |A,B |
2 |B,C |
3 |C,D |
4 |D,A |
5 |E,C |
I need number of A,B,C,D,E present in the table as follows. Please note: columns are dynamic means they are dependent upon the values in the data column from the table:
A|B|C|D|E|
2|2|3|2|1|
And I have written the following query:
SELECT id,s.data
FROM my_table t,
unnest(string_to_array(t.data, ',')) s(data);
The output is given as follows:
id|data|
1 | A |
1 | A |
1 | A |
1 | A |
1 | A |
1 | A |
1 | A |
1 | A |
Please find the query which provide exact output which you want.
select * from crosstab ('SELECT ''Total'':: text, s.data, count(*)::int
FROM my_table t, unnest(string_to_array(t.data, '','')) s(data)
group by s.data') as ct(Total text, A int, B int, C int, D int, E int)
Please create extension tablefunc (if not created).
There is a table containing hierarchical data, e.g.:
| table "attribute_instances" |
+----+----------+------------+---------------+----------+
| id | tree_ref | parent_ref | attribute_ref | data_ref |
+----+----------+------------+---------------+----------+
| 1 | 1 | -1 | 1 | 1 |
| 2 | 1 | 1 | 2 | 2 |
| 3 | 2 | -1 | 1 | 3 |
| 4 | 2 | 3 | 2 | 2 |
It contains many separate trees (see tree_ref), each of them instantiating some attributes (see attribute_ref) and have a data reference data_reference, where data might be referenced in other trees, too.
Now, those trees should be merged into a single tree, in which (by now) up to 5 attributes may be chosen as level for that tree, e.g.:
attribute => level
------------------
2 => 1
1 => 2
What I need is one or more queries, that collects the data from table attribute_instances and gives a result as follows:
| table "merged_attribute_instances" |
+----+------------+---------------+----------+
| id | parent_ref | attribute_ref | data_ref |
| 5 | -1 | 2 | 2 |
| 6 | 5 | 1 | 1 |
| 7 | 5 | 1 | 3 |
This is the desired merged tree:
id:5 - data_ref:2
id:6 - data_ref:1
id:7 - data_ref:3
Note, that attribute_ref = 2 occurs only once in the resulting tree, as all instances of it have same data_ref value (that is 2).
I've tried some joins like
select *
from attribute_instances a
join attribute_instances b on a.tree_ref = b.tree_ref
But that seems to me being bad for having user-defined tree depth. I'm sure there is a better solution.
UPDATE: I should add, that table merged_attribute_instances is a temporary table. And the collecting query is iterated with for..do. In the loop the collected attribute_instances are then added to the temporary table.
OK, then use this:
SET TERM ^ ;
create or alter procedure GETTREENODES
returns (
ID integer,
TREE_REF integer,
PARENT_REF integer,
ATTRIBUTE_REF integer,
DATA_REF integer)
as
declare variable DATAREFEXISTS varchar(4096);
begin
DATAREFEXISTS = ',';
for
Select id, tree_ref, parent_ref, attribute_ref, data_ref from attribute_instances
into :id, :tree_ref, :parent_ref, :attribute_ref, :data_ref
do begin
IF (position(',' || data_ref || ',', DATAREFEXISTS) =0) THEN
begin
suspend;
DATAREFEXISTS = DATAREFEXISTS || data_ref || ',' ;
end
end
end^
SET TERM ; ^
/* Following GRANT statetements are generated automatically */
GRANT SELECT ON ATTRIBUTE_INSTANCES TO PROCEDURE GETTREENODES;
/* Existing privileges on this procedure */
GRANT EXECUTE ON PROCEDURE GETTREENODES TO SYSDBA;
Call it like this:
Select * from gettreenodes
order by tree_ref, parent_ref
I have a table name table containing two columns foreign_table_name, and foreign_key.
Is it possible to write a SELECT query that would JOIN values of this table and the table which name is specified in the column foreign_table_name ?
For instance, if we know that all possible targetted foreign tables have a name field, I would like to know if I could write something that would:
SELECT table.foo, table.bar, foreign_table.name
FROM table
JOIN $foreign_table AS foreign_table
ON (foreign_table.id = table.foreign_key
$foreign_table = table.foreign_table);
Any solution using PlpgSQL is of course accepted.
Here's a simple content:
Table ``table``
------------------------------------------------
| foo | bar | foreign_table_name | foreign_key |
------------------------------------------------
| A | 1 | fruits | 8 |
| B | 2 | vegetable | 5 |
------------------------------------------------
Table ``fruit``
---------------
| id | name |
---------------
| 8 | apple |
---------------
Table ``vegetable``
----------------
| id | name |
----------------
| 5 | carrot |
----------------
The expected result table would be:
----------------------
| foo | bar | name |
----------------------
| A | 1 | apple |
| B | 2 | carrot |
----------------------
EDIT: I added the full table example in an attempt to be clearer.
It's usually way easier to do this sort of thing on the client side, but if you want it's possible with PL/PgSQL, e.g.
CREATE OR REPLACE FUNCTION dynamic_call(tblname text)
RETURNS TABLE (foo int, bar text, fname text)
AS $$
BEGIN
RETURN QUERY EXECUTE format('
SELECT t.foo, table.bar, f."name"
FROM mytable t
JOIN %I AS f ON (f.id = t.foreign_key);', tblname);
END;
$$ LANGUAGE plpgsql;
For more information, see the PL/PgSQL documentation.
I have a table with N columns. Let's call them c1, c2, c3, c4, ... cN. Among multiple rows, I want to get a single row with COUNT DISTINCT(cX) for each X in [1, N].
c1 | c2 | ... | cn
0 | 4 | ... | 1
Is there a way I can do this (in a stored procedure) without writing every column name into the query manually?
Why?
We've had a problem where bugs in application servers mean we rewrite good column values with garbage inserted later. To solve this, I'm storing the information log-structure, where each row represents a logical UPDATE query. Then, when given a signal that the record is complete, I can determine if any values were (erroneously) overwritten.
An example of a single correct record in multiple rows: there is at most one value for each column.
| id | initialize_time | start_time | end_time |
| 1 | 12:00am | NULL | NULL |
| 1 | 12:00am | 1:00pm | NULL |
| 1 | 12:00am | NULL | 2:00pm |
Reconciled row:
| 1 | 12:00am | 1:00pm | 2:00pm |
An example of an irreconcilable record that I want to detect:
| id | initialize_time | start_time | end_time |
| 1 | 12:00am | NULL | NULL |
| 1 | 12:00am | 1:00pm | NULL |
| 1 | 9:00am | 1:00pm | 2:00pm | -- New initialize time => irreconcilable!
You need dynamic SQL for that, which means you have to create a function or run a DO command. Since you cannot return values directly from the latter, a plpgsql function it is:
CREATE OR REPLACE function f_count_all(_tbl text
, OUT columns text[]
, OUT counts bigint[])
RETURNS record LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE (
SELECT 'SELECT
ARRAY[' || string_agg('''' || quote_ident(attname) || '''', ', ') || ']
, ARRAY[' || string_agg('count(' || quote_ident(attname) || ')' , ', ') || ']
FROM ' || _tbl
FROM pg_attribute
WHERE attrelid = _tbl::regclass
AND attnum >= 1 -- exclude tableoid & friends (neg. attnum)
AND NOT attisdropped -- exclude deleted columns
GROUP BY attrelid
)
INTO columns, counts;
END
$func$;
Call:
SELECT * FROM f_count_all('myschema.mytable');
Returns:
columns | counts
--------------+--------
{c1, c2, c3} | {17, 1, 0}
More explanation and links about dynamic SQL and EXECUTE in this related question - or a couple more here on SO, try this search.
Related:
Count values for every column in a table
You could even try and return a polymorphic record type to get single columns dynamically, but that's rather complex and advanced. Probably too much effort for your case. More in this related answer.