How can i make this mysql query ? If possible at all - mysqli

I have this table
itemname property value
------------------------------------------
A size big
A color red
B size big
B color blue
C size small
C color blue
D size small
D color blue
E size small
E color blue
Im creating a list like this: SELECT property,value,COUNT(itemname),GROUP_CONCAT(itemname) FROM table GROUP BY property,value
property value count
---------------------------------
size big 2 (A,B)
size small 3 (C,D,E)
color red 1 (A)
color blue 4 (B,C,D,E)
I want to filter the items which are BIG && SMALL && BLUE, how can i achieve this result ? (I MUST be specific about the property when addressing a value for filtering)
`SELECT
property,value,COUNT(itemname),GROUP_CONCAT(itemname)
FROM
table
GROUP BY
property,value
HAVING
( property IN ('size') && value IN ('big','small') )
&&
( property IN ('color') && value IN ('blue') )`
But this has no result, because it tries to match the row with size and color at the same time ? My desired output in this case is to avoid the item A because it is red, like this:
property value count
---------------------------------
size big 1 (B) A not here, because it is red
size small 3 (C,D,E) no change because all are blue
color red 0 (A) no red is selected, so this row should be 0 or not listed at all
color blue 4 (B,C,D,E) all the 4 are big or small and blue
Please someone help me out in this, i lost 2days wondering on the solution.
I might use CASE combined with HAVING ? Or i should address the WHERE instead somehow ?
Note: This table is actually not real, but if this question can be solved i can use it in my real tables which are a lot more complicated.

I figured a solution on my own, of course this example table is nonsense, the point is the way it had been solved.
`
SELECT
property,
value,
COUNT(t.itemname) AS count
FROM (
SELECT
itemname
FROM
table
GROUP BY
itemname
HAVING
COUNT (CASE WHEN property='size' AND value IN ('big','small') THEN 1 END) >=1
&&
COUNT (CASE WHEN property='color' AND value IN ('blue') THEN 1 END) >=1
) t
INNER JOIN
table ON table.itemname=t.itemname
GROUP BY
property,value
`

Related

How to compare values of two columns in Postgresql

I need to compare two columns which has below values and get a consolidated value in PostgreSQL. For an Id value, if all is green OR red, I want GREEN or RED to be returned respectively and even if one is RED, I want RED to be returned. Can someone please help? Thanks
ID STATUS
1 GREEN
1 GREEN
1 RED
2 GREEN
2 GREEN
2 GREEN
You seem to want string aggregation:
select id, string_agg(distinct status, ' or ' order by status) statuses
from mytable
group by id
If you want a single value returned, with priority given to "RED", then:
select id, max(status)
from mytable
group by id
This works because, string-wise, "RED" is greater than "GREEN". You don't specify what to do if there are more than two columns, so the answer does not address that.

Calculating sum with no direct join column

I have a table (ShipJourneys) where I need to calculate the Total Fuel Consumed which is a float value. See the image below.
This value is obtained by summing all the individual consumers of fuel for a given vessel over the timeframe specified. This data is contained in a second table.
Boxed in area in red shows there were 5 fuel consumers (specified by the FK_RmaDataSumsystemConfigID) and that 3 of the consumers had burned 0 units of fuel and 2 had each burned 29.
To calculate the totalFuelConsumed for that range of time frames, for a given vessel (stipulated by the FK_RmaID), the following query could be used
Select sum(FuelCalc)
from FuelCalc
where Timestamp >= '2019-07-24 00:00:00'
and Timestamp <= '2019-07-24 00:02:00'
and FK_RmaID = 660
Using something like the query below does not work, resulting in bogus values
UPDATE ShipJourneys
SET TotalFuelConsumed =
(Select sum(FuelCalc) from FuelCalc as f
WHERE f.timestamp >= StartTimeUTC
and f.timestamp <= EndTimeUTC
and f.FK_RmaID = FK_RmaID)
Any suggestions on how I could join them
You could try something like that:
UPDATE myTable // Put the table correct name here
SET TotalFuelConsumed =
Select sum(FuelUsed) from FuelTimeTbl as fuelTbl
WHERE fuelTbl.timestamp >= '2019-10-21 22:13:55.000'
and fuelTbl.imestamp <= '2019-11-27 17:10:58.000'
and fuelTbl.FK_RmaID = myTable.RmaID // Put the correct attribute name

Best way to maintain an ordered list in PostgreSQL?

Say I have a table called list, where there are items like these (the ids are random uuids):
id rank text
--- ----- -----
x 0 Hello
x 1 World
x 2 Foo
x 3 Bar
x 4 Baz
I want to maintain the property that rank column always goes from 0 to n-1 (n being the number of rows)---if a client asks to insert an item with rank = 3, then the pg server should push the current 3 and 4 to 4 and 5, respectively:
id rank text
--- ----- -----
x 0 Hello
x 1 World
x 2 Foo
x 3 New Item!
x 4 Bar
x 5 Baz
My current strategy is to have a dedicated insertion function add_item(item) that scans through the table, filter out items with rank equal or greater than that of the item being inserted, and increment those ranks by one. However, I think this approach will run into all sorts of problems---like race conditions.
Is there a more standard practice or more robust approach?
Note: The rank column is completely independent of rest of the columns, and insertion is not the only operation I need to support. Think of it as the back-end of a sortable to-do list, and the user can add/delete/reorder the items on the fly.
Doing verbatim what you suggest might be difficult or not possible at all, but I can suggest a workaround. Maintain a new column ts which stores the time a record is inserted. Then, insert the current time along with rest of the record, i.e.
id rank text ts
--- ----- ----- --------------------
x 0 Hello 2017-12-01 12:34:23
x 1 World 2017-12-03 04:20:01
x 2 Foo ...
x 3 New Item! 2017-12-12 11:26:32
x 3 Bar 2017-12-10 14:05:43
x 4 Baz ...
Now we can easily generate the ordering you want via a query:
SELECT id, rank, text,
ROW_NUMBER() OVER (ORDER BY rank, ts DESC) new_rank
FROM yourTable;
This would generate 0 to 5 ranks in the above sample table. The basic idea is to just use the already existing rank column, but to let the timestamp break the tie in ordering should the same rank appear more than once.
you can wrap it up to function if you think its worth of:
t=# with u as (
update r set rank = rank + 1 where rank >= 3
)
insert into r values('x',3,'New val!')
;
INSERT 0 1
the result:
t=# select * from r;
id | rank | text
----+------+----------
x | 0 | Hello
x | 1 | World
x | 2 | Foo
x | 3 | New val!
x | 4 | Bar
x | 5 | Baz
(6 rows)
also worth of mention you might have concurrency "chasing condition" problem on highly loaded systems. the code above is just a sample
You can have a “computed rank” which is a double precision and a “displayed rank” which is an integer that is computed using the row_number window function on output.
When a row is inserted that should rank between two rows, compute the new rank as the arithmetic mean of the two ranks.
The advantage is that you don't have to update existing rows.
The down side is that you have to calculate the displayed ranks before you can insert a new row so that you know where to insert it.
This solution (like all others) are subject to race conditions.
To deal with these, you can either use table locks or serializable transactions.
The only way to prevent a race condition would be to lock the table
https://www.postgresql.org/docs/current/sql-lock.html
Of course this would slow you down if there are lots of updates and inserts.
If can somehow limit the scope of your updates then you can do a SELECT .... FOR UPDATE on that scope. For example if the records have a parent_id you can do a select for update on the parent record first and any other insert who does the same select for update would have to wait till your transaction is done.
https://www.postgresql.org/docs/current/explicit-locking.html#:~:text=5.-,Advisory%20Locks,application%20to%20use%20them%20correctly.
Read the section on advisory locks to see if you can use those in your application. They are not enforced by the system so you'll need to be careful of how you write your application.

Tableau - Error when COUNT boolean T/F

My goal here is to count the # of True, False and NULL per day using my boolean calculation. Next step would be to calculate the percentage of each T, F, N with overall day.
Below is the visualization I would like to achieve but instead of blue dots, I would need the count of them in number.
Initially, I tried
IF [boolean] THEN 1 ELSE 0 END
But only changing from True to 1, False to 0 and missing the NULL - not counting:
I also tried different Calculated Fields to get the count of it but always getting the same error:
SUM(IF [Field]=TRUE then 1 else 0 end)
COUNT(IF [Field]=TRUE then [ID] end)
ERROR:
Could someone please assist me with a Calculate Field or any other solution where I could get a count of T, F, and NULL that would also assist me with the percentage?
Thank you
You're working too hard.
Just put SUM(Number of Records) on one shelf with DAY(Disconnected ...) on another, with the Boolean field that classifies data rows as > 0 or not on some other shelf, such as Color. No need for a table calc.
Here is an image of a solution posted at https://community.tableau.com/message/601862#601862

SQLite Group By with Order By not working

Need some stack overflow love. Read the other questions related to group by and sort by and those answers don't seem to work for me. I am not really strong in the finer details of db statements and think there is some dependency or conflict between group and order by statements I am missing.
I have a table (itemOptions) that holds all the options of an item and all the possible values for those options. Think multiple select/drop down boxes for an item and the list of values for each drop down. And unfortunately I can't change the db structure, as a web service is providing the sqlite file.
Here's the structure:
absTtemId optionName optionPosition valueName valuePosition
item1 size 1 small 1
item1 size 1 medium 2
item1 size 1 large 3
item1 color 2 white 1
item1 color 2 red 2
item1 color 2 yellow 3
item2 name 1 willis 1
item2 name 1 smith 2
item2 name 1 bowman 3
The query needs to return optionsNames for a given item ordered by optionPosition, and then a list of valueNames for each option ordered by valueposition, like this
option valueNames
size small, medium, large
color white, red, yellow
I am able to get the grouping of valueNames by option to work, but when I try to add sorting anywhere, sqlite throws errors.
Here's my current sql statement that returns these results, however option and valueNames are never in order.
SELECT optionName, group_concat(valueName)
FROM itemOptions
WHERE absItemId = 'item1'
GROUP BY optionName
option valueNames
size medium, small, large
color yellow, red, white
Here's some of my failed attempts at adding sorting for valueName and optionName
SELECT optionName, group_concat(valueName ORDER BY valuePosition DESC)
FROM itemOptions
WHERE absItemId = 'item1'
GROUP BY optionName
EDIT: sporting for optionName grouping is working now with this. Only valueName sorting within the group_concat not working.
SELECT optionName, group_concat(valueName)
FROM itemOptions
WHERE absItemId = 'item1'
GROUP BY optionName
ORDER BY optionPosition
try
SELECT a.optionName, group_concat(a.valueName)
FROM (SELECT * FROM itemOptions ORDER BY valuePosition ASC) As a
WHERE a.absItemId = 'item1'
GROUP BY a.optionName
ORDER BY a.optionName