I have a table that is similar to the following below:
id | cat | one_above | top_level |
0 'printers' 'hardware' 'computers'
I want to be able to write a query, without using unions, that will return me a result set that transposes this table's columns into rows. What this means, is that I want the result to be:
id | cat |
0 'printers'
0 'hardware'
0 'computers'
Is this possible in MySQL? I can not drop down to the application layer and perform this because I'm feeding these into a search engine that will index based on the id. Various other DBMS have something like PIVOT and UNPIVOT. I would appreciate any insight to something that I'm missing.
Mahmoud
P.S.
I'm considering re-normalization of the database as a last option, since this won't be a trivial task.
Thanks!
I got this out of the book The Art of SQL, pages 284-286:
Let's say your table name is foo.
First, create a table called pivot:
CREATE Table pivot (
count int
);
Insert into that tables as many rows as there are columns that you want to pivot in foo. Since you have three columns in foo that you want to pivot, create three rows in the pivot table:
insert into pivot values (1);
insert into pivot values (2);
insert into pivot values (3);
Now do a Cartesian join between foo and pivot, using a CASE to select the correct column based on the count:
SELECT foo.id, Case pivot.count
When 1 Then cat
When 2 Then one_above
When 3 Then top_level
End Case
FROM foo JOIN pivot;
This should give you what you want.
After some fairly extensive digging I stumbled on this page which may or may not contain your answer. It's a difficult in MySQL but from a conceptual point of view I can construct a query that would transpose like this using describe (though it would probably perform horribly). So I'm sure that we can figure out a way to do it the right way.
Related
I have a table with 20 columns that all display the same thing. I'm not sure why my company set it up like this, but I cannot make changes to the table.
With that in mind, here is what I need to do. I need to populate a drop down list with insurance company names. Therefore I need to find unique values across the entire table.
Using a Group By clause is out of the question because I need unique values across the entire table. No single column contains all the possible values. My only thought was to combine all the columns of the table together. I've seen this done using two pipes ( || ). But that concatenates the columns which does not help me.
I need to join two (or twenty) columns together and add their rows together. I.e. if I started out with 20 columns and 100 rows, I need to have one column with 2000 rows. This way I can select unique values using the Group By clause.
Any help would be greatly appreciated!
Sample of what I'm trying to accomplish:
Sample original table:
--Ins1-----Ins2---Ins3---Ins4-
Medicaid-Medicare-------------
---------Medicaid-----No 485--
Blue Cross--------------------
-------Home Health----Medicare
Table I need to construct:
--Column1--
-Medicaid--
-----------
Blue Cross-
-----------
-Medicare--
-Medicaid--
-----------
Home Health
-----------
-----------
-----------
-----------
-----------
--No 485---
-----------
-Medicare--
Maybe my logic is wrong. This is the only way I could see to find unique information across the entire table.
If this is SQL Server, then it sounds like you need to use UNPIVOT to perform this transformation.
Sample UNPIVOT (See SQL Fiddle with Demo):
select ques, answer
FROM t1
unpivot
(
answer
for ques in (col1, col2, col3, col4)
) u
The UNPIVOT will transform your data from columns to rows.
I have a table called SOURCE_TAG where I want to insert a data where all the insert statements will differ only in one of the columns (this column is a primary key id in a table called SOURCE_LU ). However, to get the id of the column I should also do some work.
The following list contains a list of stringKeys (a column in SOURCE_LU)
So first, I should do some think like the following pseudo code in Oracle SQL
stringKeys= {"foo","bar","foobar","barfoo",...,"etc"}
for(each s in StringKeys) {
SELECT SOURCE_LU where stringKeys=s and Store the id in a list (lets say idList)
}
after getting the list of id's insert each id in to SOURCE_TAG with other similar data for each row
for (each id in listId ){
INSERT INTO SOURCE_TAG values (x,y,id)
}
Sorry, I am a java guy with little SQL knowledge. So how should use Arrays, and loops in Oracle SQL? The simpler the solution the better. Thank you.
SQL itself doesn’t have loops, but Oracle has a procedural language called PL/SQL that you can use. It has loops, conditionals, variables, and other things you might be used to.
However, I think what you are trying to accomplish can be done in regular SQL. Mind you, I haven’t used an Oracle installation in years and don’t have access to one right now, but in PostgreSQL you can do something like:
INSERT INTO SOURCE_TAG
(YEAR_ID,SOURCE_TAG_LU_ID,PRIORITY_ORDER,STATUS_EN,SOURCE_LU_ID)
select 4 as year_id, 2 as source_tag, 1000 as priority_order, 'ACTIVE' as status_en, id
from source_lu
where stringkeys in ('foo', 'bar', ...)
group by year_id, source_tag, priority_order, status_en, id;
It’s possible that group by id is enough in the last line.
I have two tables which look like the following
tools:
id | part name
---------------
0 | hammer
1 | sickle
2 | axe
people:
personID | ownedTool1 | ownedTool2 | ownedTool3 ..... ownedTool20
------------------------------------------------------------------
0 | 2 | 1 | 3 ... ... 0
I'm trying to find out how many people own a particular tool. A person cannot own multiple copies of the same tool.
The only way I can think of doing this is something like
SELECT COUNT(*)
FROM tools JOIN people ON tools.id = people.ownedTool1.id OR tools.id = people.ownedTool2 ... and so on
WHERE tools.id = 0
to get the number of people who own hammers. I believe this will work, however, this involves having 20 OR statements in the query. Surely there is a more appropriate way to form such a query and I'm interested to learn how to do this.
You shouldn't have 20 columns each possibly containing an ID in the first place. You should properly establish a normalized schema. If a tool can belong to only one user - but a user can have multiple tools, you should establish a One to Many relationship. Each tool will have a user id in its row that maps back to the user it belongs to. If a tool can belong to one or more users you will need to establish a Many to Many relationship. This will require an intermediate table that contains rows of user_id to tool_id mappings. Having a schema set up appropriately like that will make the query you're looking to perform trivial.
In your particular case it seems like a user can have many tools and a tool can be "shared" by many users. For your many-to-many relation all you would have to do is count the number of rows in that intermediate table having your desired tool_id.
Something like this:
SELECT COUNT(ID) FROM UserTools Where ToolID = #desired_tool_id
Googling the terms I bolded should get you pointed in the correct direction. If you're stuck with that schema then the way you pointed out is the only way to do it.
If you cannot change the model (and I'm sure you will tell us that), then the only sensible way to work around this broken datamodel is to create a view that will give you a normalized view (pun intended) on the data:
create view normalized_people
as
select personid,
ownedTool1 as toolid
from people
union all
select personid,
ownedTool2 as toolid
from people
select personid,
ownedTool3 as toolid
from people
... you get the picture ...
Then your query is as simple as
select count(personid)
from normalized_people
where toolid = 0;
You received your (warranted) lectures about the database design.
As to your question, there is a simple way:
SELECT count(*) AS person_ct
FROM tbl t
WHERE translate((t)::text, '()', ',,')
~~ ('%,' || #desired_tool_id::text || ',%')
Or, if the first column is person_id and you want to exclude that one from the search:
SELECT count(*) AS person_ct
FROM tbl t
WHERE replace((t)::text, ')', ',')
~~ ('%,' || #desired_tool_id::text || ',%')
Explanation
Every table is accompanied by a matching composite type in PostgreSQL. So you can query any table this way:
SELECT (tbl) FROM tbl;
Yields one column per row, holding the whole row.
PostgreSQL can cast such a row type to text in one fell swoop: (tbl)::text
I replace both parens () with a comma , so every value of the row is delimited by commas ,.
My second query does not translate the opening parenthesis, so the first column (person_id) is excluded from the search.
Now I can search all columns with a simple LIKE (~~) expression using the desired number delimited by commas ~~ %,17,%
Voilá: all done with one simple command. This is reliable as long as you don't have columns like text or int[] in your table that could also hold ,17, within their values, or additional columns with numbers, which could lead to false positives.
It won't deliver performance wonders as it cannot use standard indexes. (You could create a GiST or GIN index on an expression using the tgrm module in pg 9.1, but that's another story.)
Anyway, if you want to optimize, you'd better start by normalizing your table layout as has been suggested.
I have a table which has a column for Id and parentId. ParentId contains the Id of another row in the table. If the ParentId is null then it is the top of the hierarchy.
I have the Id of a row and I want to select all rows above it in the hierarchy. Can I do this in a single select?
so in this example:
Id | parentId | other columns
1 | null
2 | 1
3 | 2
if I have id=3 I want to select rows 1,2,3.
Can I do it in linq to sql?
You can do it in a single select using a recursive CTE, however LINQ to SQL doesn't support this so you will have to create a stored procedure with the query and call that from LINQ to SQL.
Take a look at this example, uses recursive CTE.
Don't know LINQ, but as other answerers have written, many relational databases support Common Table Expressions (CTE) - but not all (Oracle comes to mind). And if supported, CTE is a good approach to retrieving the "ancestry".
That noted, there are some other approaches to consider in particular a bridge table or nested set. See my question for some explanation of these options and other ways of representing hierarchical data. Briefly, a bridge table most likely updated using CTE from a trigger will easily give you all ancestors or descendants - just not how close. A nested set model will give you this information and how close at the expense of more expensive inserts and updates comparatively.
I have a table which holds ~1M rows. My application has a list of ~100K IDs which belong to that table (the list being generated by the application layer).
Is there a common-method of how to query all of these IDs? ~100K Select queries? A temporary table which I insert the ~100K IDs to, and Select query via join the required table?
Thanks,
Doori Bar
You could do it in one query, something like
SELECT * FROM large_table WHERE id IN (...)
Insert a comma-separated list of IDs where I put the ...
Unfortunately, there is no easy way that I know of to parametrize this, so you need to be extra-super careful to avoid SQL injection vulnerabilities.
A temporary table which holds the 100k IDs seems like a good solution. Don't insert them one by one though ; INSERT ... VALUES syntax in MySQL accepts the insertion of multiple rows.
By the way, where do you get your 100k IDs, if it's not from the database ? If they come from a preceding request, I'd suggest to have it fill the temporary table.
Edit : For a more portable way of multiple insert :
INSERT INTO mytable (col1, col2) SELECT 'foo', 0 UNION SELECT 'bar', 1
Do those id's actually reference the table with 1M rows?
If so, you could use SELECT * ids FROM <1M table>
where ids is the ID column and where "1M table" is the name of the table which holds the 1M rows.
but I don't think I really understand your question...