diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 178fdb9d4c..8f5c9b0b1a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -72,8 +72,9 @@ static const uint32 PGSS_FILE_HEADER = 0x20120328;
 /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */
 #define USAGE_EXEC(duration)	(1.0)
 #define USAGE_INIT				(1.0)	/* including initial planning */
-#define USAGE_NON_EXEC_STICK	(3.0)	/* to make new entries sticky */
+#define ASSUMED_MEDIAN_INIT		(10.0)	/* initial assumed median usage */
 #define USAGE_DECREASE_FACTOR	(0.99)	/* decreased every entry_dealloc */
+#define STICKY_DECREASE_FACTOR	(0.50)	/* factor for sticky entries */
 #define USAGE_DEALLOC_PERCENT	5		/* free this % of entries at once */
 
 #define JUMBLE_SIZE				1024	/* query serialization buffer size */
@@ -139,6 +140,7 @@ typedef struct pgssSharedState
 {
 	LWLockId	lock;			/* protects hashtable search/modification */
 	int			query_size;		/* max query length in bytes */
+	double		cur_median_usage;	/* current median usage in hashtable */
 } pgssSharedState;
 
 /*
@@ -413,6 +415,7 @@ pgss_shmem_startup(void)
 		/* First time through ... */
 		pgss->lock = LWLockAssign();
 		pgss->query_size = pgstat_track_activity_query_size;
+		pgss->cur_median_usage = ASSUMED_MEDIAN_INIT;
 	}
 
 	/* Be sure everyone agrees on the hash table entry size */
@@ -908,7 +911,7 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
 /*
  * Given an arbitrarily long query string, produce a hash for the purposes of
  * identifying the query, without normalizing constants.  Used when hashing
- * utility statements, or for legacy compatibility mode.
+ * utility statements.
  */
 static uint32
 pgss_hash_string(const char *str)
@@ -951,17 +954,28 @@ pgss_store(const char *query, uint32 queryId,
 
 	entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
 
-	/*
-	 * When creating an entry just to store the normalized string, make it
-	 * artificially sticky so that it will probably still be there when
-	 * executed.  Strictly speaking, query strings are normalized on a best
-	 * effort basis, though it would be difficult to demonstrate this even
-	 * under artificial conditions.
-	 */
-	if (jstate && !entry)
-		usage = USAGE_NON_EXEC_STICK;
+	if (jstate)
+	{
+		/*
+		 * When creating an entry just to store the normalized string, make it
+		 * artificially sticky so that it will probably still be there when
+		 * the query gets executed.  We do this by giving it a median usage
+		 * value rather than the normal value.  (Strictly speaking, query
+		 * strings are normalized on a best effort basis, though it would be
+		 * difficult to demonstrate this even under artificial conditions.)
+		 * But if we found the entry already present, don't let this call
+		 * increment its usage.
+		 */
+		if (!entry)
+			usage = pgss->cur_median_usage;
+		else
+			usage = 0;
+	}
 	else
+	{
+		/* normal case, increment usage by normal amount */
 		usage = USAGE_EXEC(duration);
+	}
 
 	if (!entry)
 	{
@@ -1185,8 +1199,8 @@ pg_stat_statements(PG_FUNCTION_ARGS)
 			values[i++] = Float8GetDatumFast(tmp.time_write);
 		}
 
-		Assert(i == sql_supports_v1_1_counters ?
-			   PG_STAT_STATEMENTS_COLS : PG_STAT_STATEMENTS_COLS_V1_0);
+		Assert(i == (sql_supports_v1_1_counters ?
+					 PG_STAT_STATEMENTS_COLS : PG_STAT_STATEMENTS_COLS_V1_0));
 
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
@@ -1288,7 +1302,11 @@ entry_dealloc(void)
 	int			nvictims;
 	int			i;
 
-	/* Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them. */
+	/*
+	 * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
+	 * While we're scanning the table, apply the decay factor to the usage
+	 * values.
+	 */
 
 	entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
 
@@ -1297,10 +1315,19 @@ entry_dealloc(void)
 	while ((entry = hash_seq_search(&hash_seq)) != NULL)
 	{
 		entries[i++] = entry;
-		entry->counters.usage *= USAGE_DECREASE_FACTOR;
+		/* "Sticky" entries get a different usage decay rate. */
+		if (entry->counters.calls == 0)
+			entry->counters.usage *= STICKY_DECREASE_FACTOR;
+		else
+			entry->counters.usage *= USAGE_DECREASE_FACTOR;
 	}
 
 	qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
+
+	/* Also, record the (approximate) median usage */
+	if (i > 0)
+		pgss->cur_median_usage = entries[i / 2]->counters.usage;
+
 	nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
 	nvictims = Min(nvictims, i);