I have a flat table structure which I've turned into a column based table. I'm struggling with getting the rowId from my raw data to appear in my column based table. Any help greatly appreciated.
Raw data in table derived from three different tables:
| rowId |columnName |ColumnValue |
| ---------------- |:---------------:| -----------:|
| 1 |itemNo |1 |
| 1 |itemName |Polo Shirt |
| 1 |itemDescription |Green |
| 1 |price1 |4.2 |
| 1 |price2 |5.3 |
| 1 |price3 |7.5 |
| 1 |displayOrder |1 |
| 1 |rowId |[NULL] |
| 2 |itemNo |12 |
| 2 |itemName |Digital Watch|
| 2 |itemDescription |Red Watch |
| 2 |price1 |4.0 |
| 2 |price2 |2.0 |
| 2 |price3 |1.5 |
| 2 |displayOrder |3 |
| 2 |rowId |[NULL] |
SQL using pivot to give me the column structure:
select [displayOrder],[itemDescription],[itemName],[itemNo],[price1],[price2],[price3],[rowId]
from
(
SELECT [columnName], [columnValue] , row_number() over(partition by c.columnName order by cv.rowId) as rn
FROM tblFlatTable AS t
JOIN tblFlatColumns c
ON t.flatTableId = c.flatTableId
JOIN tblFlatColumnValues cv
ON cv.flatColumnId = c.flatColumnId
WHERE (t.flatTableId = 1) AND (t.isActive = 1)
AND (c.isActive = 1) AND (cv.isActive = 1)
) as S
Pivot
(
MIN([columnValue])
FOR columnName IN ([displayOrder],[itemDescription],[itemName],[itemNo],[price1],[price2],[price3],[rowId])
) as P
Result:
|displayOrder|itemDescription|itemName |price1|price2|price3|rowId |
| ---------- |:-------------:|:------------:|:----:|:----:|:----:|-----:|
|1 |Green |Polo Shirt |4.2 |5.3 |7.5 |[NULL]|
|3 |Red watch |Digital Watch |4.0 |2.0 |1.5 |[NULL]|
I understand why I'm getting the NULL value for rowId. What I'm stuck on and I'm not sure if it's possible to do as I've looked an many example and none seem to do this, that is to pull the value for rowId from the raw data and add it to my structure.
It looks obvious now!
I'm now not including rowId as part of my flat structure.
| rowId |columnName |ColumnValue |
| ---------------- |:---------------:| -----------:|
| 1 |itemNo |1 |
| 1 |itemName |Polo Shirt |
| 1 |itemDescription |Green |
| 1 |price1 |4.2 |
| 1 |price2 |5.3 |
| 1 |price3 |7.5 |
| 1 |displayOrder |1 |
| 2 |itemNo |12 |
| 2 |itemName |Digital Watch|
| 2 |itemDescription |Red Watch |
| 2 |price1 |4.0 |
| 2 |price2 |2.0 |
| 2 |price3 |1.5 |
| 2 |displayOrder |3 |
I've updated the SQL, you can see I'm pulling in the rowId from tblFlatColumnValues
select [rowId],[displayOrder],[itemDescription],[itemName],[itemNo],[price1],[price2],[price3]
from
(
SELECT cv.rowId, [columnName], [columnValue] , row_number() over(partition by c.columnName order by cv.rowId) as rn
FROM tblFlatTable AS t
JOIN tblFlatColumns c
ON t.flatTableId = c.flatTableId
JOIN tblFlatColumnValues cv
ON cv.flatColumnId = c.flatColumnId
WHERE (t.flatTableId = 1) AND (t.isActive = 1)
AND (c.isActive = 1) AND (cv.isActive = 1)
) as S
Pivot
(
MIN([columnValue])
FOR columnName IN ([displayOrder],[itemDescription],[itemName],[itemNo],[price1],[price2],[price3])
) as P
Related
I have this table (mock data) :
ID
Name
Location
1
Main
/
2
Photos
/1/3
3
Media
/1
4
Charts
/
5
Expenses
/4
The column Location is a string with ids that refer to that very table.
I'm looking for a query to convert ids into names, something like this :
ID
Name
Location
FullName
1
Main
/
/
2
Photos
/1/3
/Main/Media
3
Media
/1
/Main
4
Charts
/
/
5
Expenses
/4
/Charts
This is some mock data, in my real table I have more complex locations.
I'm not the owner of the table so I can't modify the schema. I can only read it.
Someone has an idea ?
Thank you very much
I've been exploring with this function : regexp_split_to_table
WITH flat_data AS (
SELECT DISTINCT
col.id col_id,
col.name col_name,
col.location col_full_loc,
regexp_split_to_table(col.location, '/') as loc_item
FROM collection col),
clean_data AS (
SELECT
col_id,
col_name,
col_full_loc,
CASE WHEN loc_item = '' THEN null ELSE loc_item::integer END loc_item,
ROW_NUMBER() over (partition by col_id, loc_item)
FROM flat_data
) select * from clean_data
So I've managed to have something like this :
| ID | Name | Location | AfterFunction |
| -- | -- | -- | -- |
| 1 | Main | / | |
| 2 | Photos | /1/3 | |
| 2 | Photos | /1/3 | 3 |
| 2 | Photos | /1/3 | |
| 2 | Photos | /1/3 | 1 |
| 3 | Media | /1 | |
| 3 | Media | /1 | 1 |
| 4 | Charts | / | |
| 5 | Expenses | /4 | |
| 5 | Expenses | /4 | 4 |
But at some point I lose the order of sublocation item
EDIT : table style
Outlook to the solution
ignore the first slash in the location to simplify the split and mapping (add it again at the end)
use regexp_split_to_table along with WITH ORDINALITY to preserve the order
outer join the location part to the original table (cast the idto textis it is int)
string_agg the location names to one string using the ordinality column and add the fixed slash prefix.
Query
with t2 as (
select * from t,
regexp_split_to_table(substr(t.location,2), '/') WITH ORDINALITY x(part, rn)
),
t3 as (
select t2.*, t.name part_name from t2
left outer join t on t2.part = t.id::text)
select
t3.id, t3.name, t3.location,
'/'||coalesce(string_agg(t3.part_name,'/' order by t3.rn),'') loc_name
from t3
group by 1,2,3
order by 1
gives result
id|name |location|loc_name |
--+--------+--------+-----------+
1|Main |/ |/ |
2|Photos |/1/3 |/Main/Media|
3|Media |/1 |/Main |
4|Charts |/ |/ |
5|Expenses|/4 |/Charts |
Below the result of the subqueries to illustrated the steps
-- T2
id|name |location|part|rn|
--+--------+--------+----+--+
1|Main |/ | | 1|
2|Photos |/1/3 |1 | 1|
2|Photos |/1/3 |3 | 2|
3|Media |/1 |1 | 1|
4|Charts |/ | | 1|
5|Expenses|/4 |4 | 1|
-- T3
id|name |location|part|rn|part_name|
--+--------+--------+----+--+---------+
1|Main |/ | | 1|Main |
2|Photos |/1/3 |1 | 1|Photos |
2|Photos |/1/3 |3 | 2|Photos |
3|Media |/1 |1 | 1|Media |
4|Charts |/ | | 1|Charts |
5|Expenses|/4 |4 | 1|Expenses |
I have several tables
table1
-------------------------
|id| rec | other_rec|
-------------------------
|1 | record1 | record6 |
|2 | record2 | record8 |
|3 | record4 | record0 |
|4 | record5 | record2 |
|n | ... | ... |
------------------------
and a second table
table2
-------------------------------------------------
|id| table_nr_1_foreign_key | rec_1 | rec_2 |
-------------------------------------------------
|1 | table_nr_1_key_1 |record1 | rt1 |
|2 | table_nr_1_key_2 |record2 | rt2 |
|3 | table_nr_1_key_2 |record4 | rt3 |
|4 | table_nr_1_key_3 |record5 | rt4 |
|5 | table_nr_1_key_2 |record6 | rt5 |
|n | table_nr_1_key_n | ... | ... |
-------------------------------------------------
and an SQL query
SELECT t.id,
t.rec,
t.other_rec,
t2.rec_1
FROM table1 t1
JOIN ...[other table]
and ...[conditions start]
and ...
and ...[conditions end]
LEFT JOIN table2 t2 ON t.id = table_nr_1_foreign_key
AND t2.rec_2 = any(array['rt2'])
where ...[other condition]
now I want to select all records from table2 which have a corresponding foreign key in a table1
if and only if at least one record exists in a table2 that points to a table1.
so I want
data1,..., rt2
data1,..., rt3
data1,..., rt5
...
to be selected
but all I get is
data1,..., rt2
Update no 1.
with my inexperience I failed to accomplish selection with one sql and wrote two sql queries instead
for data selection for a main record (table 1)
the other for selecting all records from a table 2 (using in([list
of ids]))
This question can be closed / deleted.
So... I am currently using Oracle 11.1g and I need to create a query that uses the ID and CusCODE from Table_with_value and checks Table_with_status using the ID to find active CO_status but on different CusCODE.
This is what I have so far - obviously does not work as it should unless CusCODE and ID are provided manually:
SELECT wm_concat(CoID) as active_CO_Status_for_same_ID_but_different_CusCODE
FROM Table_with_status
WHERE
CoID IN (SELECT CoID FROM Table_with_status WHERE ID = Table_with_value.ID AND CusCODE != Table_with_value.CusCODE)) AND Co_status = 'active';
Table_with_value:
|CoID | CusCODE | ID | Value |
|--------|---------|----------|----|
|354223 | 1.432 | 0784296L | 99 |
|321232 | 4.212321.22 | 0432296L | 32 |
|938421 | 3.213 | 0021321L | 93 |
Table_with_status:
|CoID | CusCODE | ID | Co_status|
|--------|--------------|----------|--------|
|354223 | 1.432 | 0784296L | active|
|354232 | 1.432 | 0784296L | inactive |
|666698 | 1.47621 | 0784296L | active |
|666700 | 1.5217 | 0784296L | active |
|938421 | 3.213 | 0021321L | active |
|938422 | 3.213 | 0021321L | active |
|938423 | 3.213 | 0021321L | active |
|321232 | 4.212321.22 | 0432296L | active |
|321232 | 4.212321.22 | 0432296L | active |
|321232 | 1.689 | 0432296L | inactive |
Expected output:
|CoID | active_CO_Status_for_same_ID_but_different_CusCODE | ID | Value |
|--------|---------|----------|----|
|354223 | 666698,666700 | 1.432 | 0784296L | 99 |
|321232 | N/A | 4.212321.22 | 0432296L | 32 |
|938421 | N/A | 3.213 | 0021321L | 93 |
Any idea on how this can be implemented ideally without any PL/SQL for loops, but it should be fine as well since the output dataset is expected < 300 IDs.
I apologize in advance for the cryptic nature in which I structured the question :) Let me know if something is not clear.
From your description and expected output, it looks like you need a left outer join, something like:
SELECT v.CoID,
wm_concat(s.CoID) as other_active_CusCODE -- active_CO_Status_for_same_ID_but_different_CusCODE
v.CusCODE,
v.ID,
v.value
FROM Table_with_value v
LEFT JOIN Table_with_status s
ON s.ID = v.ID
AND s.CusCODE != v.CusCODE
AND s.Co_status = 'active'
GROUP BY v.CoID, v.CusCODE, v.ID, v.value;
SQL Fiddle using listagg() instead of the never-supported and now-removed wm_concat(); with a couple of different approaches if the logic isn't quite what I interpreted. With your sample data they all get:
COID OTHER_ACTIVE_CUSCODE CUSCODE ID VALUE
------ -------------------- ----------- -------- -----
321232 (null) 4.212321.22 0432296L 32
354223 666698,666700 1.432 0784296L 99
938421 (null) 3.213 0021321L 93
Your code looks like it should work, assuming you are referring to the correct tables:
SELECT wm_concat(s.CoID) as active_CO_Status_for_same_ID_but_different_CusCODE
FROM Table_with_status s
WHERE s.CoID IN (SELECT v.CoID
FROM Table_with_value v
WHERE v.ID = s.ID AND
v.CusCODE <> s.CusCODE
) AND
s.Co_status = 'active';
I want to count how many times the first and last column of a sqlite file are the same for each row in my data set. the data set has 16+ million rows and efficiency is very important.
I have tried:
SELECT * FROM tab WHERE [0] = [3]
but it doesn't work. probably because it compares the first column of each row with the last column of the same row.
Let's assume this is my data set:
0 |1 |2 |3 |
--------------------------------------
2005:67 |ytg |6utgjgt |786:09 |
2005:903 |467 |009 |2005:67 |
2005:444 |355 |785 |2005:450|
2005:450 |355 |785 |N/A |
2005:934 |467 |009 |N/A |
2005:000 |355 |785 |2005:450|
2005:987 |355 |785 |2005:450|
--------------------------------------
the output should be this:
0 |1 |2 |3 |4 |
-----------------------------------------------
2005:67 |ytg |6utgjgt |786:09 |1 |
2005:450 |355 |785 |N/A |3 |
2005:934 |467 |009 |N/A |0 |
-----------------------------------------------
the rows whose 4th column were the same as the first column of one of the rows are dropped but were counted. (It is not possible that the 4th column of a row is the same as the first column of more than one row. And the first column's values for each row are identical)
Can everybody please help me? I am a rookie and greatly appreciate some explanation along with the code. Thank you
With NOT EXISTS:
select t.*,
(select count(*) from tab where [3] = t.[0]) [4]
from tab t
where not exists (
select 1 from tab
where [0] = t.[3]
)
See the demo.
Results:
| 0 | 1 | 2 | 3 | 4 |
| -------- | --- | ------- | ------ | --- |
| 2005:67 | ytg | 6utgjgt | 786:09 | 1 |
| 2005:450 | 355 | 785 | N/A | 3 |
| 2005:934 | 467 | 009 | N/A | 0 |
I have 3 tables like this.
Entity_Table
|e_id|e_name|e_type |e_tenant|
|1 | Bob | bird | owner_1|
|2 | Joe | cat | owner_1|
|3 | Joe | cat | owner_2|
AttributeValue_Table
|av_id|prop_name |prop_value|
|1 | color | black |
|2 | color | white |
|3 | wing size| 7" |
|4 | whiskers | long |
|5 | whiskers | short |
|6 | random | anything |
Entity_AttrVal
|e_id|av_id|
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 2 | 5 |
| 3 | 1 |
| 3 | 4 |
| 3 | 6 |
What I want to be able to do is something like 'find entity where e_name='Joe' and color=black and whiskers=short.
I can obtain a result set where each row has 1 prop/value, along with the entity information, so querying on one property works. But I need to be able to do arbitrary N properties. How do I do something like this?
Can I build a join table with all properties as columns or something
edit2: Looks like I can do something like this
SELECT et.e_id, et.e_name, et.e_type
FROM Entitiy_table et
LEFT JOIN Entity_AttrVal j ON et.e_id = j.e_id
RIGHT JOIN AttributeValue_Table at ON at.av_id = j.av_id
WHERE (av.prop_name='color' AND av.prop_value='white') OR (av.prop_name='whiskers' AND av.prop_value='long')
GROUP BY et.e_id, et.e_name, et.e_type
HAVING COUNT(*) = 2;
You have to add a predicate for each name/value combination:
SELECT <whatever you need>
FROM Entity_Table et
WHERE et.e_name = 'Joe'
AND EXISTS (SELECT 1
FROM AttributeValue_Table avt
JOIN Entity_AttrVal ea ON ea.e_id = et.e_id
WHERE ea.a_id = avt.av_id
AND avt.prop_name = 'color'
AND avt.prop_value = 'black')
AND EXISTS (SELECT 1
FROM AttributeValue_Table avt
JOIN Entity_AttrVal ea ON ea.e_id = et.e_id
WHERE ea.a_id = avt.av_id
AND avt.prop_name = 'whiskers'
AND avt.prop_value = 'short')
(I apologize if my Sql Server dialect shines through)
To do an arbitrary number of comparisons, you'd have to generate the SQL and execute it.
As said in a comment, this goes to show that EAV is a pain (an anti-pattern, really), but I know by experience that sometimes there's simply no alternative if we're bound to a relational database.