Consider the following HoH:
$h = {
a => {
1 => x
},
b => {
2 => y
},
...
}
Is there a way to check whether a hash key exists on the second nested level without calling keys(%$h)? For example, I want to say something like:
if ( exists($h->{*}->{1}) ) { ...
(I realize you can't use * as a hash key wildcard, but you get the idea...)
I'm trying to avoid using keys() because it will reset the hash iterator and I am iterating over $h in a loop using:
while ( (my ($key, $value) = each %$h) ) {
...
}
The closest language construct I could find is the smart match operator (~~) mentioned here (and no mention in the perlref perldoc), but even if ~~ was available in the version of Perl I'm constrained to using (5.8.4), from what I can tell it wouldn't work in this case.
If it can't be done I suppose I'll copy the keys into an array or hash before entering my while loop (which is how I started), but I was hoping to avoid the overhead.
Not really. If you need to do that, I think I'd create a merged hash listing all the second level keys (before starting your main loop):
my $h = {
a => {
1 => 'x'
},
b => {
2 => 'y'
},
};
my %all = map { %$_ } values %$h;
Then your exists($h->{*}->{1}) becomes exists($all{1}). Of course, this won't work if you're modifying the second-level hashes inside the loop (unless you update %all appropriately). The code also assumes that all values in $h are hashrefs, but that would be easy to fix if necessary.
No. each uses the hash's iterator, and you cannot iterate over a hash without using its iterator, not even in the C API. (That means smart match wouldn't help anyway.)
Since each hash has its own iterator, you must be calling keys on the same hash that you are already iterating over using each to run into this problem. Since you have no problem calling keys on that hash, could you just simply use keys instead of each? Or maybe call keys once, store the result, then iterate over the stored keys?
You will almost certainly find that the 'overhead' of aggregating the second-level hashes is less than that of any other solution. A simple hash lookup is far faster than iterating over the entire data structure every time you want to make the check.
are you trying to do this without any while loop? You can test for existence in a hash just by referencing it, without generating an error
while ( my ($key, $value) = each %{$h} ) {
if ($value->{1}) { .. }
}
Why not do this in Sybase itself instead of Perl?
You are trying to do a set operation which is what Sybase is built to do in the first place.
Assuming you retrieved the data from table with columns "key1", "key2", "valye" as "select *", simply do:
-- Make sure mytable has index on key1
SELECT key1
FRIN mytable t1
WHERE NOT EXISTS (
SELECT 1 FROM mytable t2
WHERE t1.key1=t2.key1
AND t2.key2 = 1
)
-----------
-- OR
-----------
SELECT DISTINCT key1
INTO #t
FROM mytable
CREATE INDEX idx1_t on #t (key1)
DELETE #t
FROM mytable
WHERE #t.key1=mytable.key1
AND mytable.key2 = 1
SELECT key1 from #t
Either query returns a list of 1st level keys that don't have key2 of 1
Related
I have an array of strings, some of which may be repeated. I am trying to build a query which returns a single json object where the keys are the distinct values in the array, and the values are the count of times each value appears in the array.
I have built the following query;
WITH items (item) as (SELECT UNNEST(ARRAY['a','b','c','a','a','a','c']))
SELECT json_object_agg(distinct_values, counts) item_counts
FROM (
SELECT
sub2.distinct_values,
count(items.item) counts
FROM (
SELECT DISTINCT items.item AS distinct_values
FROM items
) sub2
JOIN items ON items.item = sub2.distinct_values
GROUP BY sub2.distinct_values, items.item
) sub1
DbFiddle
Which provides the result I'm looking for: { "a" : 4, "b" : 1, "c" : 2 }
However, it feels like there's probably a better / more elegant / less verbose way of achieving the same thing, so I wondered if any one could point me in the right direction.
For context, I would like to use this as part of a bigger more complex query, but I didn't want to complicate the question with irrelevant details. The array of strings is what one column of the query currently returns, and I would like to convert it into this JSON blob. If it's easier and quicker to do it in code then I can, but I wanted to see if there was an easy way to do it in postgres first.
I think a CTE and json_object_agg() is a little bit of a shortcut to get you there?
WITH counter AS (
SELECT UNNEST(ARRAY['a','b','c','a','a','a','c']) AS item, COUNT(*) AS item_count
GROUP BY 1
ORDER BY 1
)
SELECT json_object_agg(item, item_count) FROM counter
Output:
{"a":4,"b":1,"c":2}
Suppose I want to do a bulk update, setting a=b for a collection of a values. This can easily be done with a sequence of UPDATE queries:
UPDATE foo SET value='foo' WHERE id=1
UPDATE foo SET value='bar' WHERE id=2
UPDATE foo SET value='baz' WHERE id=3
But now I suppose I want to do this in bulk. I have a two dimensional array containing the ids and new values:
[ [ 1, 'foo' ]
[ 2, 'bar' ]
[ 3, 'baz' ] ]
Is there an efficient way to do these three UPDATEs in a single SQL query?
Some solutions I have considered:
A temporary table
CREATE TABLE temp ...;
INSERT INTO temp (id,value) VALUES (....);
UPDATE foo USING temp ...
But this really just moves the problem. Although it may be easier (or at least less ugly) to do a bulk INSERT, there are still a minimum of three queries.
Denormalize the input by passing the data pairs as SQL arrays. This makes the query incredibly ugly, though
UPDATE foo
USING (
SELECT
split_part(x,',',1)::INT AS id,
split_part(x,',',2)::VARCHAR AS value
FROM (
SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz']) AS x
) AS x;
)
SET value=x.value WHERE id=x.id
This makes it possible to use a single query, but makes that query ugly, and inefficient (especially for mixed and/or complex data types).
Is there a better solution? Or should I resort to multiple UPDATE queries?
Normally you want to batch-update from a table with sufficient index to make the merge easy:
CREATE TEMP TABLE updates_table
( id integer not null primary key
, val varchar
);
INSERT into updates_table(id, val) VALUES
( 1, 'foo' ) ,( 2, 'bar' ) ,( 3, 'baz' )
;
UPDATE target_table t
SET value = u.val
FROM updates_table u
WHERE t.id = u.id
;
So you should probably populate your update_table by something like:
INSERT into updates_table(id, val)
SELECT
split_part(x,',',1)::INT AS id,
split_part(x,',',2)::VARCHAR AS value
FROM (
SELECT UNNEST(ARRAY['1,foo','2,bar','3,baz'])
) AS x
;
Remember: an index (or the primary key) on the id field in the updates_table is important. (but for small sets like this one, a hashjoin will probably by chosen by the optimiser)
In addition: for updates, it is important to avoid updates with the same value, these cause extra rowversions to be created + plus the resulting VACUUM activity after the update was committed:
UPDATE target_table t
SET value = u.val
FROM updates_table u
WHERE t.id = u.id
AND (t.value IS NULL OR t.value <> u.value)
;
You can use CASE conditional expression:
UPDATE foo
SET "value" = CASE id
WHEN 1 THEN 'foo'
WHEN 2 THEN 'bar'
WHEN 3 THEN 'baz'
END
According to ZF documentation when using fetchAssoc() the first column in the result set must contain unique values, or else rows with duplicate values in the first column will overwrite previous data.
I don't want this, I want my array to be indexed 0,1,2,3... I don't need rows to be unique because I won't modify them and won't save them back to the DB.
According to ZF documentation fetchAll() (when using the default fetch mode, which is in fact FETCH_ASSOC) is equivalent to fetchAssoc(). BUT IT'S NOT.
I've used print_r()function to reveal the truth.
print_r($db->fetchAll('select col1, col2 from table'));
prints
Array
(
[0] => Array
(
[col1] => 1
[col2] => 2
)
)
So:
fetchAll() is what I wanted.
There's a bug in ZF documentation
From http://framework.zend.com/manual/1.11/en/zend.db.adapter.html
The fetchAssoc() method returns data in an array of associative arrays, regardless of what value you have set for the fetch mode, **using the first column as the array index**.
So if you put
$result = $db->fetchAssoc(
'SELECT some_column, other_column FROM table'
);
you'll have as result an array like this
$result['some_column']['other_column']
I'm working on an Active Record pattern (similar to RoR/Cake) for my Zend Framework library. My question is this: How do I figure out whether a select object is using an alias for a table or not?
$select->from(array("c" => "categories"));
vs.
$select->from("categories");
and I pass this to a "fetch" function which adds additional joins and whatnot to get the row relationships automatically...I want to add some custom sql; either "c.id" or "categories.id" based on how the user used the "from" method.
I know I can use
$parts = $select->getPart(Zend_Db_Select::FROM);
to get the from data as an array, and the table name or alias seems to be in "slot" 0 of said array. Will the table name or alias always be in slot zero? i.e. can I reliably use:
$tableNameOrAlias = $parts[0];
Sorry if this is convolute but hope you can help! :)
Logically, I would think that's how it should work. To be on the safe side, build a few dummy queries using a Select() and dump the part array using print_r or such.
I just performed this test, the alias is the array key, it is not a zero-based numeric array:
$select = $this->db->select()->from(array("c" => "categories","d" => "dummies"));
$parts = $select->getPart(Zend_Db_Select::FROM);
echo '<pre>';
print_r($parts);
echo '</pre>';
Output:
Array
(
[c] => Array
(
[joinType] => inner join
[schema] =>
[tableName] => categories
[joinCondition] =>
)
)
So you would need to reference it as $part["c"]
I am having a lot of IDs and I want to store them for a temporary purpose and need to search that record for some operation. Which data structure is good for this operation in Perl? Should I use a hash or an array, or is there any other module I could use to do this efficiently?
The records are 4343, 34343, 34343, 2323, 232, ....
A little more information regarding your record layout would go a long way in helping people help you. If your records are linked to id numbers then you can use a hash with the 'id' as the key and store the record as a string or an array reference as the hash value
my %records;
$records{ $id_number } = "Record for $id_number";
## OR
$records{ $id_number } = ['Record', 'for', $id_number];
This will allow you to lookup id's with complexity O(1) and easily manipulate the corresponding record.
# Assuming the records are stored in #records
for my $record (#records) {
$recStore{$record}++;
}
# To search for a record
my $recToFind = 4343;
my $recExists = $recStore{$recToFind} || 0;
The keys of the hash are the id's retrieved from your database and the corresponding values are the number of times the id was found, so for repeating records $recExists will be greater than 1, and for non-existent records it will be equal to 0. To get a list of all id's sorted numerically you could write
my #sortedID = sort {$a <=> $b} keys %records;